diff --git a/.travis.yml b/.travis.yml index b9b187ea6a8..241ead6f1c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,11 +21,5 @@ notifications: skip_join: true channels: - "irc.freenode.org#spree" -branches: - only: - - 1-0-stable - - 1-1-stable - - master rvm: - - 1.8.7 - - 1.9.3 + - 1.9.3 \ No newline at end of file diff --git a/README.md b/README.md index 2851fd10356..df308004827 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -**THIS README IS FOR THE MASTER BRANCH OF SPREE AND REFLECTS THE WORK CURRENTLY EXISTING ON THE MASTER BRANCH. IF YOU ARE WISHING TO USE A NON-MASTER BRANCH OF -SPREE, PLEASE CONSULT THAT BRANCH'S README AND NOT THIS ONE.** - SUMMARY ------- @@ -32,9 +29,9 @@ Installation The fastest way to get started is by using the spree command line tool available in the spree gem which will add Spree to an existing Rails application. - $ gem install rails -v 3.2.7 + $ gem install rails -v 3.2.9 $ gem install spree - $ rails _3.2.7_ new my_store + $ rails _3.2.9_ new my_store $ spree install my_store This will add the Spree gem to your Gemfile, create initializers, copy migrations and @@ -100,21 +97,21 @@ The source code is essentially a collection of gems. Spree is meant to be run w 1. Clone the Git repo - git clone git://github.com/spree/spree.git - cd spree + $ git clone git://github.com/spree/spree.git + $ cd spree 2. Install the gem dependencies - bundle install + $ bundle install 3. Create a sandbox Rails application for testing purposes (and automatically perform all necessary database setup) - bundle exec rake sandbox + $ bundle exec rake sandbox 4. Start the server - cd sandbox - rails server + $ cd sandbox + $ rails server Performance ----------- @@ -134,7 +131,7 @@ Use Dedicated Spree Devise Authentication Add the following to your Gemfile $ gem 'spree_auth_devise', :git => 'git://github.com/spree/spree_auth_devise' - + Then run `bundle install`. Authentication will then work exactly as it did in previous versions of Spree. If you're installing this in a new Spree 1.2+ application, you'll need to install and run the migrations with @@ -142,6 +139,14 @@ If you're installing this in a new Spree 1.2+ application, you'll need to instal $ bundle exec rake spree_auth:install:migrations $ bundle exec rake db:migrate +change the following line in `config/initializers/spree.rb` +```ruby +Spree.user_class = "Spree::LegacyUser" +``` +to +```ruby +Spree.user_class = "Spree::User" +``` and then run `bundle exec rake spree_auth:admin:create` in order to set up the admin user for the application. Running Tests diff --git a/Rakefile b/Rakefile index 5e1df6ac5a7..8ca3d1053f5 100644 --- a/Rakefile +++ b/Rakefile @@ -87,5 +87,7 @@ end desc "Creates a sandbox application for simulating the Spree code in a deployed Rails app" task :sandbox do - exec("lib/sandbox.sh") + Bundler.with_clean_env do + exec("lib/sandbox.sh") + end end diff --git a/SPREE_VERSION b/SPREE_VERSION index a5b53b3152e..23aa8390630 100644 --- a/SPREE_VERSION +++ b/SPREE_VERSION @@ -1 +1 @@ -1.2.0.rc1 +1.2.2 diff --git a/api/app/controllers/spree/api/v1/addresses_controller.rb b/api/app/controllers/spree/api/v1/addresses_controller.rb index c22596b2652..aa708d0ffb9 100644 --- a/api/app/controllers/spree/api/v1/addresses_controller.rb +++ b/api/app/controllers/spree/api/v1/addresses_controller.rb @@ -4,10 +4,12 @@ module V1 class AddressesController < Spree::Api::V1::BaseController def show @address = Address.find(params[:id]) + authorize! :read, @address end def update @address = Address.find(params[:id]) + authorize! :read, @address @address.update_attributes(params[:address]) render :show, :status => 200 end diff --git a/api/app/controllers/spree/api/v1/base_controller.rb b/api/app/controllers/spree/api/v1/base_controller.rb index 4881d401cbb..6b44d12806a 100644 --- a/api/app/controllers/spree/api/v1/base_controller.rb +++ b/api/app/controllers/spree/api/v1/base_controller.rb @@ -6,7 +6,8 @@ class BaseController < ActionController::Metal attr_accessor :current_api_user - before_filter :check_for_api_key + before_filter :set_content_type + before_filter :check_for_api_key, :if => :requires_authentication? before_filter :authenticate_user rescue_from CanCan::AccessDenied, :with => :unauthorized @@ -25,13 +26,28 @@ def map_nested_attributes_keys(klass, attributes) private + def set_content_type + content_type = case params[:format] + when "json" + "application/json" + when "xml" + "text/xml" + end + headers["Content-Type"] = content_type + end + def check_for_api_key render "spree/api/v1/errors/must_specify_api_key", :status => 401 and return if api_key.blank? end def authenticate_user - unless @current_api_user = Spree.user_class.find_by_spree_api_key(api_key) - render "spree/api/v1/errors/invalid_api_key", :status => 401 and return + if requires_authentication? || api_key.present? + unless @current_api_user = Spree.user_class.find_by_spree_api_key(api_key) + render "spree/api/v1/errors/invalid_api_key", :status => 401 and return + end + else + # Effectively, an anonymous user + @current_api_user = Spree.user_class.new end end @@ -39,6 +55,10 @@ def unauthorized render "spree/api/v1/errors/unauthorized", :status => 401 and return end + def requires_authentication? + Spree::Api::Config[:requires_authentication] + end + def not_found render "spree/api/v1/errors/not_found", :status => 404 and return end diff --git a/api/app/controllers/spree/api/v1/countries_controller.rb b/api/app/controllers/spree/api/v1/countries_controller.rb index 8435596d5a1..55f72e9cd25 100644 --- a/api/app/controllers/spree/api/v1/countries_controller.rb +++ b/api/app/controllers/spree/api/v1/countries_controller.rb @@ -3,7 +3,10 @@ module Api module V1 class CountriesController < Spree::Api::V1::BaseController def index - @countries = Country.includes(:states).order('name ASC') + @countries = Country. + ransack(params[:q]).result. + includes(:states).order('name ASC'). + page(params[:page]).per(params[:per_page]) end def show diff --git a/api/app/controllers/spree/api/v1/images_controller.rb b/api/app/controllers/spree/api/v1/images_controller.rb index b8df78a4d79..12d0fc94f70 100644 --- a/api/app/controllers/spree/api/v1/images_controller.rb +++ b/api/app/controllers/spree/api/v1/images_controller.rb @@ -7,20 +7,23 @@ def show end def create + authorize! :create, Image @image = Image.create(params[:image]) render :show, :status => 201 end def update + authorize! :update, Image @image = Image.find(params[:id]) @image.update_attributes(params[:image]) render :show, :status => 200 end def destroy + authorize! :delete, Image @image = Image.find(params[:id]) @image.destroy - render :text => nil + render :text => nil, :status => 204 end end diff --git a/api/app/controllers/spree/api/v1/line_items_controller.rb b/api/app/controllers/spree/api/v1/line_items_controller.rb index 28dfd2b866d..90c2c9a5fad 100644 --- a/api/app/controllers/spree/api/v1/line_items_controller.rb +++ b/api/app/controllers/spree/api/v1/line_items_controller.rb @@ -26,7 +26,7 @@ def destroy authorize! :read, order @line_item = order.line_items.find(params[:id]) @line_item.destroy - render :text => nil, :status => 200 + render :text => nil, :status => 204 end private diff --git a/api/app/controllers/spree/api/v1/orders_controller.rb b/api/app/controllers/spree/api/v1/orders_controller.rb index dddac3aabba..55ea57d2f95 100644 --- a/api/app/controllers/spree/api/v1/orders_controller.rb +++ b/api/app/controllers/spree/api/v1/orders_controller.rb @@ -2,31 +2,25 @@ module Spree module Api module V1 class OrdersController < Spree::Api::V1::BaseController - before_filter :map_nested_attributes, :only => [:create, :update] before_filter :authorize_read!, :except => [:index, :search, :create] def index # should probably look at turning this into a CanCan step raise CanCan::AccessDenied unless current_api_user.has_spree_role?("admin") - @orders = Order.page(params[:page]).per(params[:per_page]) + @orders = Order.ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) end def show end - def search - @orders = Order.ransack(params[:q]).result.page(params[:page]) - render :index - end - def create - @order = Order.build_from_api(current_api_user, @nested_params) - next! + @order = Order.build_from_api(current_api_user, nested_params) + next!(:status => 201) end def update authorize! :update, Order - if order.update_attributes(@nested_params) + if order.update_attributes(nested_params) order.update! render :show else @@ -35,8 +29,8 @@ def update end def address - order.build_ship_address(params[:shipping_address]) - order.build_bill_address(params[:billing_address]) + order.build_ship_address(params[:shipping_address]) if params[:shipping_address] + order.build_bill_address(params[:billing_address]) if params[:billing_address] next! end @@ -64,17 +58,17 @@ def empty private - def map_nested_attributes - @nested_params = map_nested_attributes_keys Order, params[:order] + def nested_params + map_nested_attributes_keys Order, params[:order] || {} end def order @order ||= Order.find_by_number!(params[:id]) end - def next! + def next!(options={}) if @order.valid? && @order.next - render :show, :status => 200 + render :show, :status => options[:status] || 200 else render :could_not_transition, :status => 422 end diff --git a/api/app/controllers/spree/api/v1/payments_controller.rb b/api/app/controllers/spree/api/v1/payments_controller.rb index e33386c5010..8b14446088e 100644 --- a/api/app/controllers/spree/api/v1/payments_controller.rb +++ b/api/app/controllers/spree/api/v1/payments_controller.rb @@ -6,7 +6,7 @@ class PaymentsController < Spree::Api::V1::BaseController before_filter :find_payment, :only => [:show, :authorize, :purchase, :capture, :void, :credit] def index - @payments = @order.payments + @payments = @order.payments.ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) end def new @@ -29,6 +29,10 @@ def authorize perform_payment_action(:authorize) end + def capture + perform_payment_action(:capture) + end + def purchase perform_payment_action(:purchase) end diff --git a/api/app/controllers/spree/api/v1/product_properties_controller.rb b/api/app/controllers/spree/api/v1/product_properties_controller.rb new file mode 100644 index 00000000000..b2b7508251a --- /dev/null +++ b/api/app/controllers/spree/api/v1/product_properties_controller.rb @@ -0,0 +1,64 @@ +module Spree + module Api + module V1 + class ProductPropertiesController < Spree::Api::V1::BaseController + before_filter :find_product + before_filter :product_property, :only => [:show, :update, :destroy] + + def index + @product_properties = @product.product_properties. + ransack(params[:q]).result + .page(params[:page]).per(params[:per_page]) + end + + def show + end + + def new + end + + def create + authorize! :create, ProductProperty + @product_property = @product.product_properties.new(params[:product_property]) + if @product_property.save + render :show, :status => 201 + else + invalid_resource!(@product_property) + end + end + + def update + authorize! :update, ProductProperty + if @product_property && @product_property.update_attributes(params[:product_property]) + render :show, :status => 200 + else + invalid_resource!(@product_property) + end + end + + def destroy + authorize! :delete, ProductProperty + if(@product_property) + @product_property.destroy + render :text => nil, :status => 204 + else + invalid_resource!(@product_property) + end + + end + + private + def find_product + @product = super(params[:product_id]) + end + + def product_property + if @product + @product_property ||= @product.product_properties.find_by_id(params[:id]) + @product_property ||= @product.product_properties.joins(:property).where('spree_properties.name' => params[:id]).readonly(false).first + end + end + end + end + end +end diff --git a/api/app/controllers/spree/api/v1/products_controller.rb b/api/app/controllers/spree/api/v1/products_controller.rb index 9967742309c..99722e91715 100644 --- a/api/app/controllers/spree/api/v1/products_controller.rb +++ b/api/app/controllers/spree/api/v1/products_controller.rb @@ -3,12 +3,7 @@ module Api module V1 class ProductsController < Spree::Api::V1::BaseController def index - @products = product_scope.page(params[:page]) - end - - def search - @products = product_scope.ransack(params[:q]).result.page(params[:page]) - render :index + @products = product_scope.ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) end def show @@ -44,7 +39,7 @@ def destroy @product = find_product(params[:id]) @product.update_attribute(:deleted_at, Time.now) @product.variants_including_master.update_all(:deleted_at => Time.now) - render :text => nil, :status => 200 + render :text => nil, :status => 204 end end end diff --git a/api/app/controllers/spree/api/v1/return_authorizations_controller.rb b/api/app/controllers/spree/api/v1/return_authorizations_controller.rb new file mode 100644 index 00000000000..b8accbb3346 --- /dev/null +++ b/api/app/controllers/spree/api/v1/return_authorizations_controller.rb @@ -0,0 +1,53 @@ +module Spree + module Api + module V1 + class ReturnAuthorizationsController < Spree::Api::V1::BaseController + before_filter :authorize_admin! + + def index + @return_authorizations = order.return_authorizations. + ransack(params[:q]).result. + page(params[:page]).per(params[:per_page]) + end + + def show + @return_authorization = order.return_authorizations.find(params[:id]) + end + + def create + @return_authorization = order.return_authorizations.build(params[:return_authorization], :as => :api) + if @return_authorization.save + render :show, :status => 201 + else + invalid_resource!(@return_authorization) + end + end + + def update + @return_authorization = order.return_authorizations.find(params[:id]) + if @return_authorization.update_attributes(params[:return_authorization]) + render :show + else + invalid_resource!(@return_authorization) + end + end + + def destroy + @return_authorization = order.return_authorizations.find(params[:id]) + @return_authorization.destroy + render :text => nil, :status => 204 + end + + private + + def order + @order ||= Order.find_by_number!(params[:order_id]) + end + + def authorize_admin! + authorize! :manage, Spree::ReturnAuthorization + end + end + end + end +end diff --git a/api/app/controllers/spree/api/v1/shipments_controller.rb b/api/app/controllers/spree/api/v1/shipments_controller.rb index 2f44e970754..ecd7f3629d5 100644 --- a/api/app/controllers/spree/api/v1/shipments_controller.rb +++ b/api/app/controllers/spree/api/v1/shipments_controller.rb @@ -6,6 +6,7 @@ class ShipmentsController < BaseController before_filter :find_and_update_shipment, :only => [:ship, :ready] def ready + authorize! :read, Shipment unless @shipment.ready? @shipment.ready! end @@ -13,6 +14,7 @@ def ready end def ship + authorize! :read, Shipment unless @shipment.shipped? @shipment.ship! end @@ -23,6 +25,7 @@ def ship def find_order @order = Spree::Order.find_by_number!(params[:order_id]) + authorize! :read, @order end def find_and_update_shipment diff --git a/api/app/controllers/spree/api/v1/taxonomies_controller.rb b/api/app/controllers/spree/api/v1/taxonomies_controller.rb index e3180018056..d1d802ecfc5 100644 --- a/api/app/controllers/spree/api/v1/taxonomies_controller.rb +++ b/api/app/controllers/spree/api/v1/taxonomies_controller.rb @@ -3,7 +3,10 @@ module Api module V1 class TaxonomiesController < Spree::Api::V1::BaseController def index - @taxonomies = Taxonomy.order('name').includes(:root => :children) + @taxonomies = Taxonomy. + order('name').includes(:root => :children). + ransack(params[:q]).result. + page(params[:page]).per(params[:per_page]) end def show @@ -32,7 +35,7 @@ def update def destroy authorize! :delete, Taxonomy taxonomy.destroy - render :text => nil, :status => 200 + render :text => nil, :status => 204 end private diff --git a/api/app/controllers/spree/api/v1/taxons_controller.rb b/api/app/controllers/spree/api/v1/taxons_controller.rb index bed8b1118ca..96f3235e6bc 100644 --- a/api/app/controllers/spree/api/v1/taxons_controller.rb +++ b/api/app/controllers/spree/api/v1/taxons_controller.rb @@ -32,7 +32,7 @@ def update def destroy authorize! :delete, Taxon taxon.destroy - render :text => nil, :status => 200 + render :text => nil, :status => 204 end private diff --git a/api/app/controllers/spree/api/v1/variants_controller.rb b/api/app/controllers/spree/api/v1/variants_controller.rb index 655c986fe38..694e7adbca4 100644 --- a/api/app/controllers/spree/api/v1/variants_controller.rb +++ b/api/app/controllers/spree/api/v1/variants_controller.rb @@ -5,7 +5,9 @@ class VariantsController < Spree::Api::V1::BaseController before_filter :product def index - @variants = scope.includes(:option_values).scoped + @variants = scope. + includes(:option_values).ransack(params[:q]).result. + page(params[:page]).per(params[:per_page]) end def show @@ -39,7 +41,7 @@ def destroy authorize! :delete, Variant @variant = scope.find(params[:id]) @variant.destroy - render :text => nil, :status => 200 + render :text => nil, :status => 204 end private @@ -48,7 +50,23 @@ def product end def scope - @product ? @product.variants_including_master : Variant + if @product + unless current_api_user.has_spree_role?("admin") || params[:show_deleted] + variants = @product.variants_including_master + else + variants = @product.variants_including_master_and_deleted + end + else + variants = Variant.scoped + if current_api_user.has_spree_role?("admin") + unless params[:show_deleted] + variants = Variant.active + end + else + variants = variants.active + end + end + variants end end end diff --git a/api/app/controllers/spree/api/v1/zones_controller.rb b/api/app/controllers/spree/api/v1/zones_controller.rb index 2b5def5df18..ec33f4f65db 100644 --- a/api/app/controllers/spree/api/v1/zones_controller.rb +++ b/api/app/controllers/spree/api/v1/zones_controller.rb @@ -3,7 +3,7 @@ module Api module V1 class ZonesController < Spree::Api::V1::BaseController def index - @zones = Zone.order('name ASC') + @zones = Zone.order('name ASC').ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) end def show @@ -32,7 +32,7 @@ def update def destroy authorize! :delete, Zone zone.destroy - render :text => nil, :status => 200 + render :text => nil, :status => 204 end private diff --git a/api/app/helpers/spree/api/api_helpers.rb b/api/app/helpers/spree/api/api_helpers.rb index 51d97b8a1fa..8cbf5b5784d 100644 --- a/api/app/helpers/spree/api/api_helpers.rb +++ b/api/app/helpers/spree/api/api_helpers.rb @@ -63,6 +63,14 @@ def taxonomy_attributes def taxon_attributes [:id, :name, :permalink, :position, :parent_id, :taxonomy_id] end + + def return_authorization_attributes + [:id, :number, :state, :amount, :order_id, :reason, :created_at, :updated_at] + end + + def country_attributes + [:id, :iso_name, :iso, :iso3, :name, :numcode] + end end end end diff --git a/api/app/models/spree/api_configuration.rb b/api/app/models/spree/api_configuration.rb new file mode 100644 index 00000000000..a9d5d733ef4 --- /dev/null +++ b/api/app/models/spree/api_configuration.rb @@ -0,0 +1,5 @@ +module Spree + class ApiConfiguration < Preferences::Configuration + preference :requires_authentication, :boolean, :default => true + end +end diff --git a/api/app/models/spree/order_decorator.rb b/api/app/models/spree/order_decorator.rb index c52cdb3b8cc..34e8a2599d1 100644 --- a/api/app/models/spree/order_decorator.rb +++ b/api/app/models/spree/order_decorator.rb @@ -1,6 +1,7 @@ Spree::Order.class_eval do def self.build_from_api(user, params) order = create + params[:line_items_attributes] ||= [] params[:line_items_attributes].each do |line_item| order.add_variant(Spree::Variant.find(line_item[:variant_id]), line_item[:quantity]) end diff --git a/api/app/views/spree/api/v1/countries/index.rabl b/api/app/views/spree/api/v1/countries/index.rabl index cfb3010e608..028c4ad0f3f 100644 --- a/api/app/views/spree/api/v1/countries/index.rabl +++ b/api/app/views/spree/api/v1/countries/index.rabl @@ -1,2 +1,7 @@ -collection @countries -extends "spree/api/v1/countries/show" \ No newline at end of file +object false +child(@countries => :countries) do + attributes *country_attributes +end +node(:count) { @countries.count } +node(:current_page) { params[:page] || 1 } +node(:pages) { @countries.num_pages } diff --git a/api/app/views/spree/api/v1/countries/show.rabl b/api/app/views/spree/api/v1/countries/show.rabl index 4e3bf830420..19d4322a9c3 100644 --- a/api/app/views/spree/api/v1/countries/show.rabl +++ b/api/app/views/spree/api/v1/countries/show.rabl @@ -1,5 +1,5 @@ object @country -attributes :id, :iso_name, :iso, :iso3, :name, :numcode +attributes *country_attributes child :states => :states do attributes :id, :name, :abbr, :country_id -end \ No newline at end of file +end diff --git a/api/app/views/spree/api/v1/orders/index.rabl b/api/app/views/spree/api/v1/orders/index.rabl index 033dead6a1f..c937d2bffcf 100644 --- a/api/app/views/spree/api/v1/orders/index.rabl +++ b/api/app/views/spree/api/v1/orders/index.rabl @@ -1,5 +1,5 @@ object false -child(@orders) do +child(@orders => :orders) do attributes *order_attributes end node(:count) { @orders.count } diff --git a/api/app/views/spree/api/v1/orders/show.rabl b/api/app/views/spree/api/v1/orders/show.rabl index b9b335cc48c..8920e75ad8b 100644 --- a/api/app/views/spree/api/v1/orders/show.rabl +++ b/api/app/views/spree/api/v1/orders/show.rabl @@ -1,6 +1,9 @@ object @order attributes *order_attributes -extends "spree/api/v1/orders/#{@order.state}" + +if lookup_context.find_all("spree/api/v1/orders/#{@order.state}").present? + extends "spree/api/v1/orders/#{@order.state}" +end child :billing_address => :bill_address do extends "spree/api/v1/addresses/show" diff --git a/api/app/views/spree/api/v1/payments/index.rabl b/api/app/views/spree/api/v1/payments/index.rabl index 3d9b2a44f85..f72651a0d53 100644 --- a/api/app/views/spree/api/v1/payments/index.rabl +++ b/api/app/views/spree/api/v1/payments/index.rabl @@ -1,2 +1,7 @@ -collection @payments -attributes *payment_attributes +object false +child(@payments => :payments) do + attributes *payment_attributes +end +node(:count) { @payments.count } +node(:current_page) { params[:page] || 1 } +node(:pages) { @payments.num_pages } diff --git a/api/app/views/spree/api/v1/product_properties/index.rabl b/api/app/views/spree/api/v1/product_properties/index.rabl new file mode 100644 index 00000000000..3f81a99d7b6 --- /dev/null +++ b/api/app/views/spree/api/v1/product_properties/index.rabl @@ -0,0 +1,7 @@ +object false +child(@product_properties => :product_properties) do + attributes *product_property_attributes +end +node(:count) { @product_properties.count } +node(:current_page) { params[:page] || 1 } +node(:pages) { @product_properties.num_pages } diff --git a/api/app/views/spree/api/v1/product_properties/new.rabl b/api/app/views/spree/api/v1/product_properties/new.rabl new file mode 100644 index 00000000000..9556ea41320 --- /dev/null +++ b/api/app/views/spree/api/v1/product_properties/new.rabl @@ -0,0 +1,2 @@ +node(:attributes) { [*product_property_attributes] } +node(:required_attributes) { [] } \ No newline at end of file diff --git a/api/app/views/spree/api/v1/product_properties/show.rabl b/api/app/views/spree/api/v1/product_properties/show.rabl new file mode 100644 index 00000000000..12e24137b53 --- /dev/null +++ b/api/app/views/spree/api/v1/product_properties/show.rabl @@ -0,0 +1,2 @@ +object @product_property +attributes *product_property_attributes diff --git a/api/app/views/spree/api/v1/products/index.rabl b/api/app/views/spree/api/v1/products/index.rabl index 8797e5e4f54..54107ca7530 100644 --- a/api/app/views/spree/api/v1/products/index.rabl +++ b/api/app/views/spree/api/v1/products/index.rabl @@ -1,5 +1,6 @@ object false -node(:count) { @products.total_count } +node(:count) { @products.count } +node(:total_count) { @products.total_count } node(:current_page) { params[:page] ? params[:page].to_i : 1 } node(:pages) { @products.num_pages } child(@products) do diff --git a/api/app/views/spree/api/v1/return_authorizations/index.rabl b/api/app/views/spree/api/v1/return_authorizations/index.rabl new file mode 100644 index 00000000000..6574912a6d0 --- /dev/null +++ b/api/app/views/spree/api/v1/return_authorizations/index.rabl @@ -0,0 +1,7 @@ +object false +child(@return_authorizations => :return_authorizations) do + attributes *return_authorization_attributes +end +node(:count) { @return_authorizations.count } +node(:current_page) { params[:page] || 1 } +node(:pages) { @return_authorizations.num_pages } diff --git a/api/app/views/spree/api/v1/return_authorizations/new.rabl b/api/app/views/spree/api/v1/return_authorizations/new.rabl new file mode 100644 index 00000000000..362fa8ac9de --- /dev/null +++ b/api/app/views/spree/api/v1/return_authorizations/new.rabl @@ -0,0 +1,3 @@ +object false +node(:attributes) { [*return_authorization_attributes] } +node(:required_attributes) { required_fields_for(Spree::ReturnAuthorization) } diff --git a/api/app/views/spree/api/v1/return_authorizations/show.rabl b/api/app/views/spree/api/v1/return_authorizations/show.rabl new file mode 100644 index 00000000000..00b07a3b1c2 --- /dev/null +++ b/api/app/views/spree/api/v1/return_authorizations/show.rabl @@ -0,0 +1,2 @@ +object @return_authorization +attributes *return_authorization_attributes \ No newline at end of file diff --git a/api/app/views/spree/api/v1/taxonomies/index.rabl b/api/app/views/spree/api/v1/taxonomies/index.rabl index f12f6acd9c3..0a279e048d7 100644 --- a/api/app/views/spree/api/v1/taxonomies/index.rabl +++ b/api/app/views/spree/api/v1/taxonomies/index.rabl @@ -1,2 +1,7 @@ -collection @taxonomies -extends "spree/api/v1/taxonomies/show" +object false +child(@taxonomies => :taxonomies) do + extends "spree/api/v1/taxonomies/show" +end +node(:count) { @taxonomies.count } +node(:current_page) { params[:page] || 1 } +node(:pages) { @taxonomies.num_pages } diff --git a/api/app/views/spree/api/v1/variants/index.rabl b/api/app/views/spree/api/v1/variants/index.rabl index 7790118a0e4..d37c886a2a0 100644 --- a/api/app/views/spree/api/v1/variants/index.rabl +++ b/api/app/views/spree/api/v1/variants/index.rabl @@ -1,3 +1,10 @@ -collection @variants -attributes *variant_attributes -child(:option_values => :option_values) { attributes *option_value_attributes } +object false +node(:count) { @variants.count } +node(:total_count) { @variants.total_count } +node(:current_page) { params[:page] ? params[:page].to_i : 1 } +node(:pages) { @variants.num_pages } + +child(@variants => :variants) do + attributes *variant_attributes + child(:option_values => :option_values) { attributes *option_value_attributes } +end diff --git a/api/app/views/spree/api/v1/zones/index.rabl b/api/app/views/spree/api/v1/zones/index.rabl index b16039b448b..232ed4e2395 100644 --- a/api/app/views/spree/api/v1/zones/index.rabl +++ b/api/app/views/spree/api/v1/zones/index.rabl @@ -1,2 +1,7 @@ -collection @zones -extends 'spree/api/v1/zones/show' +object false +child(@zones => :zones) do + extends 'spree/api/v1/zones/show' +end +node(:count) { @zones.count } +node(:current_page) { params[:page] || 1 } +node(:pages) { @zones.num_pages } diff --git a/api/config/routes.rb b/api/config/routes.rb index ea2cfa6348c..4e201f28011 100644 --- a/api/config/routes.rb +++ b/api/config/routes.rb @@ -11,22 +11,16 @@ namespace :api do scope :module => :v1 do resources :products do - collection do - get :search - end - resources :variants + resources :product_properties end resources :images - resources :variants, :only => [:index] do end resources :orders do - collection do - get :search - end + resources :return_authorizations member do put :address put :delivery @@ -38,6 +32,7 @@ resources :payments do member do put :authorize + put :capture put :purchase put :void put :credit diff --git a/api/lib/spree/api/engine.rb b/api/lib/spree/api/engine.rb index 8b7ee9e2212..502da59daaa 100644 --- a/api/lib/spree/api/engine.rb +++ b/api/lib/spree/api/engine.rb @@ -6,6 +6,10 @@ class Engine < Rails::Engine isolate_namespace Spree engine_name 'spree_api' + initializer "spree.api.environment", :before => :load_config_initializers do |app| + Spree::Api::Config = Spree::ApiConfiguration.new + end + def self.activate Dir.glob(File.join(File.dirname(__FILE__), "../../../app/**/*_decorator*.rb")) do |c| Rails.configuration.cache_classes ? require(c) : load(c) diff --git a/api/spec/controllers/spree/api/v1/addresses_controller_spec.rb b/api/spec/controllers/spree/api/v1/addresses_controller_spec.rb index 293fee15d50..1ff3175556a 100644 --- a/api/spec/controllers/spree/api/v1/addresses_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/addresses_controller_spec.rb @@ -9,15 +9,37 @@ module Spree @address = create(:address) end - it "gets an address" do - api_get :show, :id => @address.id - json_response['address']['address1'].should eq @address.address1 + context "with their own address" do + before do + Address.any_instance.stub :user => current_api_user + end + + it "gets an address" do + api_get :show, :id => @address.id + json_response['address']['address1'].should eq @address.address1 + end + + it "updates an address" do + api_put :update, :id => @address.id, + :address => { :address1 => "123 Test Lane" } + json_response['address']['address1'].should eq '123 Test Lane' + end end - it "updates an address" do - api_put :update, :id => @address.id, - :address => { :address1 => "123 Test Lane" } - json_response['address']['address1'].should eq '123 Test Lane' + context "on somebody else's address" do + before do + Address.any_instance.stub :user => stub_model(Spree::LegacyUser) + end + + it "cannot retreive address information" do + api_get :show, :id => @address.id + assert_unauthorized! + end + + it "cannot update address information" do + api_get :update, :id => @address.id + assert_unauthorized! + end end end end diff --git a/api/spec/controllers/spree/api/v1/base_controller_spec.rb b/api/spec/controllers/spree/api/v1/base_controller_spec.rb index a604465cbe9..9723bddd7bb 100644 --- a/api/spec/controllers/spree/api/v1/base_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/base_controller_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe Spree::Api::V1::BaseController do + render_views controller(Spree::Api::V1::BaseController) do def index render :json => { "products" => [] } diff --git a/api/spec/controllers/spree/api/v1/countries_controller_spec.rb b/api/spec/controllers/spree/api/v1/countries_controller_spec.rb index fb198f20393..3493d7e5c1b 100644 --- a/api/spec/controllers/spree/api/v1/countries_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/countries_controller_spec.rb @@ -12,7 +12,31 @@ module Spree it "gets all countries" do api_get :index - json_response.first['country']['iso3'].should eq @country.iso3 + json_response["countries"].first['country']['iso3'].should eq @country.iso3 + end + + context "with two countries" do + before { @zambia = create(:country, :name => "Zambia") } + + it "can view all countries" do + api_get :index + json_response['count'].should == 2 + json_response['current_page'].should == 1 + json_response['pages'].should == 1 + end + + it 'can query the results through a paramter' do + api_get :index, :q => { :name_cont => 'zam' } + json_response['count'].should == 1 + json_response['countries'].first['country']['name'].should eq @zambia.name + end + + it 'can control the page size through a parameter' do + api_get :index, :per_page => 1 + json_response['count'].should == 1 + json_response['current_page'].should == 1 + json_response['pages'].should == 2 + end end it "includes states" do diff --git a/api/spec/controllers/spree/api/v1/images_controller_spec.rb b/api/spec/controllers/spree/api/v1/images_controller_spec.rb index fc5da37b2ce..876fa79c4d7 100644 --- a/api/spec/controllers/spree/api/v1/images_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/images_controller_spec.rb @@ -13,43 +13,53 @@ module Spree stub_authentication! end - it "can upload a new image for a product" do - lambda do - api_post :create, - :image => { :attachment => upload_image("thinking-cat.jpg"), - :viewable_type => 'Spree::Product', - :viewable_id => product.id } - response.status.should == 201 - json_response.should have_attributes(attributes) - end.should change(Image, :count).by(1) - end + context "as an admin" do + sign_in_as_admin! + + it "can upload a new image for a variant" do + lambda do + api_post :create, + :image => { :attachment => upload_image('thinking-cat.jpg'), + :viewable_type => 'Spree::Variant', + :viewable_id => product.master.to_param } + response.status.should == 201 + json_response.should have_attributes(attributes) + end.should change(Image, :count).by(1) + end + + context "working with an existing image" do + let!(:product_image) { product.master.images.create!(:attachment => image('thinking-cat.jpg')) } - it "can upload a new image for a variant" do - lambda do - api_post :create, - :image => { :attachment => upload_image("thinking-cat.jpg"), - :viewable_type => 'Spree::Variant', - :viewable_id => product.master.to_param } - response.status.should == 201 - json_response.should have_attributes(attributes) - end.should change(Image, :count).by(1) + it "can update image data" do + product_image.position.should == 1 + api_post :update, :image => { :position => 2 }, :id => product_image.id + response.status.should == 200 + json_response.should have_attributes(attributes) + product_image.reload.position.should == 2 + end + + it "can delete an image" do + api_delete :destroy, :id => product_image.id + response.status.should == 204 + lambda { product_image.reload }.should raise_error(ActiveRecord::RecordNotFound) + end + end end - context "working with an existing image" do - let!(:product_image) { product.master.images.create!(:attachment => image("thinking-cat.jpg")) } + context "as a non-admin" do + it "cannot create an image" do + api_post :create + assert_unauthorized! + end - it "can update image data" do - product_image.position.should == 1 - api_post :update, :image => { :position => 2 }, :id => product_image.id - response.status.should == 200 - json_response.should have_attributes(attributes) - product_image.reload.position.should == 2 + it "cannot update an image" do + api_put :update, :id => 1 + assert_unauthorized! end - it "can delete an image" do - api_delete :destroy, :id => product_image.id - response.status.should == 200 - lambda { product_image.reload }.should raise_error(ActiveRecord::RecordNotFound) + it "cannot delete an image" do + api_delete :destroy, :id => 1 + assert_unauthorized! end end end diff --git a/api/spec/controllers/spree/api/v1/line_items_controller_spec.rb b/api/spec/controllers/spree/api/v1/line_items_controller_spec.rb index b1dd46e5075..d6da43d21ff 100644 --- a/api/spec/controllers/spree/api/v1/line_items_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/line_items_controller_spec.rb @@ -47,7 +47,7 @@ module Spree it "can delete a line item on the order" do line_item = order.line_items.first api_delete :destroy, :id => line_item.id - response.status.should == 200 + response.status.should == 204 lambda { line_item.reload }.should raise_error(ActiveRecord::RecordNotFound) end end diff --git a/api/spec/controllers/spree/api/v1/orders_controller_spec.rb b/api/spec/controllers/spree/api/v1/orders_controller_spec.rb index 9631c3c98f6..8ae65bf2c54 100644 --- a/api/spec/controllers/spree/api/v1/orders_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/orders_controller_spec.rb @@ -28,6 +28,13 @@ module Spree json_response.should have_attributes(attributes) end + # Regression test for #1992 + it "can view an order not in a standard state" do + Order.any_instance.stub :user => current_api_user + order.update_column(:state, 'shipped') + api_get :show, :id => order.to_param + end + it "can not view someone else's order" do Order.any_instance.stub :user => stub_model(Spree::LegacyUser) api_get :show, :id => order.to_param @@ -54,7 +61,7 @@ module Spree it "can create an order" do variant = create(:variant) api_post :create, :order => { :line_items => [{ :variant_id => variant.to_param, :quantity => 5 }] } - response.status.should == 200 + response.status.should == 201 order = Order.last order.line_items.count.should == 1 order.line_items.first.variant.should == variant @@ -62,6 +69,13 @@ module Spree json_response["order"]["state"].should == "address" end + it "can create an order without any parameters" do + lambda { api_post :create }.should_not raise_error(NoMethodError) + response.status.should == 201 + order = Order.last + json_response["order"]["state"].should == "address" + end + context "working with an order" do before do Order.any_instance.stub :user => current_api_user @@ -97,6 +111,15 @@ def clean_address(address) json_response["order"]["shipping_methods"].should_not be_empty end + it "can add just shipping address information to an order" do + api_put :address, :id => order.to_param, :shipping_address => shipping_address + response.status.should == 200 + order.reload + order.shipping_address.reload + order.shipping_address.firstname.should == shipping_address[:firstname] + order.bill_address.should be_nil + end + it "cannot use an address that has no valid shipping methods" do shipping_method.destroy api_put :address, :id => order.to_param, :shipping_address => shipping_address, :billing_address => billing_address @@ -165,6 +188,14 @@ def clean_address(address) context "as an admin" do sign_in_as_admin! + context "with no orders" do + before { Spree::Order.delete_all } + it "still returns a root :orders key" do + api_get :index + json_response["orders"].should == [] + end + end + context "with two orders" do before { create(:order) } @@ -187,6 +218,25 @@ def clean_address(address) end end + context "search" do + before do + create(:order) + Spree::Order.last.update_attribute(:email, 'spree@spreecommerce.com') + end + + let(:expected_result) { Spree::Order.last } + + it "can query the results through a parameter" do + api_get :index, :q => { :email_cont => 'spree' } + json_response["orders"].count.should == 1 + json_response["orders"].first.should have_attributes(attributes) + json_response["orders"].first["order"]["email"].should == expected_result.email + json_response["count"].should == 1 + json_response["current_page"].should == 1 + json_response["pages"].should == 1 + end + end + context "can cancel an order" do before do order.completed_at = Time.now diff --git a/api/spec/controllers/spree/api/v1/payments_controller_spec.rb b/api/spec/controllers/spree/api/v1/payments_controller_spec.rb index dea1ff11daa..e2ef5589f13 100644 --- a/api/spec/controllers/spree/api/v1/payments_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/payments_controller_spec.rb @@ -2,10 +2,11 @@ module Spree describe Spree::Api::V1::PaymentsController do + render_views let!(:order) { create(:order) } let!(:payment) { create(:payment, :order => order) } let!(:attributes) { [:id, :source_type, :source_id, :amount, - :payment_method_id, :response_code, :state, :avs_response, + :payment_method_id, :response_code, :state, :avs_response, :created_at, :updated_at] } let(:resource_scoping) { { :order_id => order.to_param } } @@ -21,7 +22,7 @@ module Spree it "can view the payments for their order" do api_get :index - json_response.first.should have_attributes(attributes) + json_response["payments"].first.should have_attributes(attributes) end it "can learn how to create a new payment" do @@ -66,7 +67,29 @@ module Spree it "can view the payments on any order" do api_get :index response.status.should == 200 - json_response.first.should have_attributes(attributes) + json_response["payments"].first.should have_attributes(attributes) + end + + context "multiple payments" do + before { @payment = create(:payment, :order => order, :response_code => '99999') } + + it "can view all payments on an order" do + api_get :index + json_response["count"].should == 2 + end + + it 'can control the page size through a parameter' do + api_get :index, :per_page => 1 + json_response['count'].should == 1 + json_response['current_page'].should == 1 + json_response['pages'].should == 2 + end + + it 'can query the results through a paramter' do + api_get :index, :q => { :response_code_cont => '999' } + json_response['count'].should == 1 + json_response['payments'].first['payment']['response_code'].should eq @payment.response_code + end end context "for a given payment" do @@ -88,6 +111,24 @@ module Spree payment.state.should == "failed" end + it "can capture" do + api_put :capture, :id => payment.to_param + response.status.should == 200 + payment.reload + payment.state.should == "completed" + end + + it "returns a 422 status when purchasing fails" do + fake_response = stub(:success? => false, :to_s => "Insufficient funds") + Spree::Gateway::Bogus.any_instance.should_receive(:capture).and_return(fake_response) + api_put :capture, :id => payment.to_param + response.status.should == 422 + json_response["error"].should == "There was a problem with the payment gateway: Insufficient funds" + + payment.reload + payment.state.should == "pending" + end + it "can purchase" do api_put :purchase, :id => payment.to_param response.status.should == 200 diff --git a/api/spec/controllers/spree/api/v1/product_properties_controller_spec.rb b/api/spec/controllers/spree/api/v1/product_properties_controller_spec.rb new file mode 100644 index 00000000000..4da171ce53e --- /dev/null +++ b/api/spec/controllers/spree/api/v1/product_properties_controller_spec.rb @@ -0,0 +1,117 @@ +require 'spec_helper' +require 'shared_examples/protect_product_actions' + +module Spree + describe Spree::Api::V1::ProductPropertiesController do + render_views + + let!(:product) { create(:product) } + let!(:property_1) {product.product_properties.create(:property_name => "My Property 1", :value => "my value 1")} + let!(:property_2) {product.product_properties.create(:property_name => "My Property 2", :value => "my value 2")} + + let(:attributes) { [:id, :product_id, :property_id, :value, :property_name] } + let(:resource_scoping) { { :product_id => product.to_param } } + + before do + stub_authentication! + end + + context "if product is deleted" do + before do + product.update_column(:deleted_at, Time.now) + end + + it "can not see a list of product properties" do + api_get :index + response.status.should == 404 + end + end + + it "can see a list of all product properties" do + api_get :index + json_response["product_properties"].count.should eq 2 + json_response["product_properties"].first.should have_attributes(attributes) + end + + it "can control the page size through a parameter" do + api_get :index, :per_page => 1 + json_response['product_properties'].count.should == 1 + json_response['current_page'].should == 1 + json_response['pages'].should == 2 + end + + it 'can query the results through a paramter' do + Spree::ProductProperty.last.update_attribute(:value, 'loose') + property = Spree::ProductProperty.last + api_get :index, :q => { :value_cont => 'loose' } + json_response['count'].should == 1 + json_response['product_properties'].first['product_property']['value'].should eq property.value + end + + it "can see a single product_property" do + api_get :show, :id => property_1.property_name + json_response.should have_attributes(attributes) + end + + it "can learn how to create a new product property" do + api_get :new + json_response["attributes"].should == attributes.map(&:to_s) + json_response["required_attributes"].should be_empty + end + + it "cannot create a new product property if not an admin" do + api_post :create, :product_property => { :property_name => "My Property 3" } + assert_unauthorized! + end + + it "cannot update a product property" do + api_put :update, :id => property_1.property_name, :product_property => { :value => "my value 456" } + assert_unauthorized! + end + + it "cannot delete a product property" do + api_delete :destroy, :property_name => property_1.property_name + assert_unauthorized! + lambda { property_1.reload }.should_not raise_error + end + + context "as an admin" do + sign_in_as_admin! + + it "can create a new product property" do + expect do + api_post :create, :product_property => { :property_name => "My Property 3", :value => "my value 3" } + end.to change(product.product_properties, :count).by(1) + json_response.should have_attributes(attributes) + response.status.should == 201 + end + + it "can update a product property" do + api_put :update, :id => property_1.property_name, :product_property => { :value => "my value 456" } + response.status.should == 200 + end + + it "can delete a variant" do + api_delete :destroy, :id => property_1.property_name + response.status.should == 204 + lambda { property_1.reload }.should raise_error(ActiveRecord::RecordNotFound) + end + end + + context "with product identified by id" do + let(:resource_scoping) { { :product_id => product.id } } + it "can see a list of all product properties" do + api_get :index + json_response["product_properties"].count.should eq 2 + json_response["product_properties"].first.should have_attributes(attributes) + end + + it "can see a single product_property by id" do + api_get :show, :id => property_1.id + json_response.count.should eq 1 + json_response.should have_attributes(attributes) + end + end + + end +end diff --git a/api/spec/controllers/spree/api/v1/products_controller_spec.rb b/api/spec/controllers/spree/api/v1/products_controller_spec.rb index 59c0c9904c0..5a2d4a735c6 100644 --- a/api/spec/controllers/spree/api/v1/products_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/products_controller_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'shared_examples/protect_product_actions' module Spree describe Spree::Api::V1::ProductsController do @@ -33,15 +34,24 @@ module Spree second_product = create(:product) api_get :index, :page => 2 json_response["products"].first.should have_attributes(attributes) - json_response["count"].should == 2 + json_response["total_count"].should == 2 json_response["current_page"].should == 2 json_response["pages"].should == 2 end + + it 'can control the page size through a parameter' do + create(:product) + api_get :index, :per_page => 1 + json_response['count'].should == 1 + json_response['total_count'].should == 2 + json_response['current_page'].should == 1 + json_response['pages'].should == 2 + end end it "can search for products" do create(:product, :name => "The best product in the world") - api_get :search, :q => { :name_cont => "best" } + api_get :index, :q => { :name_cont => "best" } json_response["products"].first.should have_attributes(attributes) json_response["count"].should == 1 end @@ -105,20 +115,7 @@ module Spree required_attributes.should include("price") end - it "cannot create a new product if not an admin" do - api_post :create, :product => { :name => "Brand new product!" } - assert_unauthorized! - end - - it "cannot update a product" do - api_put :update, :id => product.to_param, :product => { :name => "I hacked your store!" } - assert_unauthorized! - end - - it "cannot delete a product" do - api_delete :destroy, :id => product.to_param - assert_unauthorized! - end + it_behaves_like "modifying product actions are restricted" end context "as an admin" do @@ -156,6 +153,25 @@ module Spree response.status.should == 201 end + # Regression test for #2140 + context "with authentication_required set to false" do + before do + Spree::Api::Config.requires_authentication = false + end + + after do + Spree::Api::Config.requires_authentication = true + end + + it "can still create a product" do + api_post :create, :product => { :name => "The Other Product", + :price => 19.99 }, + :token => "fake" + json_response.should have_attributes(attributes) + response.status.should == 201 + end + end + it "cannot create a new product with invalid attributes" do api_post :create, :product => {} response.status.should == 422 @@ -180,7 +196,7 @@ module Spree it "can delete a product" do product.deleted_at.should be_nil api_delete :destroy, :id => product.to_param - response.status.should == 200 + response.status.should == 204 product.reload.deleted_at.should_not be_nil end end diff --git a/api/spec/controllers/spree/api/v1/return_authorizations_controller_spec.rb b/api/spec/controllers/spree/api/v1/return_authorizations_controller_spec.rb new file mode 100644 index 00000000000..c054c7018ea --- /dev/null +++ b/api/spec/controllers/spree/api/v1/return_authorizations_controller_spec.rb @@ -0,0 +1,155 @@ +require 'spec_helper' + +module Spree + describe Api::V1::ReturnAuthorizationsController do + render_views + + let!(:order) do + order = create(:order) + order.line_items << create(:line_item) + order.shipments << create(:shipment, :state => 'shipped') + order.finalize! + order.shipments.each(&:ready!) + order.shipments.each(&:ship!) + order + end + + let(:product) { create(:product) } + let(:attributes) { [:id, :reason, :amount, :state] } + let(:resource_scoping) { { :order_id => order.to_param } } + + before do + stub_authentication! + end + + context "as the order owner" do + before do + Order.any_instance.stub :user => current_api_user + end + + it "cannot see any return authorizations" do + api_get :index + assert_unauthorized! + end + + it "cannot see a single return authorization" do + api_get :show, :id => 1 + assert_unauthorized! + end + + it "cannot learn how to create a new return authorization" do + api_get :new + assert_unauthorized! + end + + it "cannot create a new return authorization" do + api_post :create + assert_unauthorized! + end + + it "cannot update a return authorization" do + api_put :update + assert_unauthorized! + end + + it "cannot delete a return authorization" do + api_delete :destroy + assert_unauthorized! + end + end + + context "as an admin" do + sign_in_as_admin! + + it "can show return authorization" do + order.return_authorizations << create(:return_authorization) + return_authorization = order.return_authorizations.first + api_get :show, :order_id => order.id, :id => return_authorization.id + response.status.should == 200 + json_response.should have_attributes(attributes) + json_response["return_authorization"]["state"].should_not be_blank + end + + it "can get a list of return authorizations" do + order.return_authorizations << create(:return_authorization) + order.return_authorizations << create(:return_authorization) + api_get :index, { :order_id => order.id } + response.status.should == 200 + return_authorizations = json_response["return_authorizations"] + return_authorizations.first.should have_attributes(attributes) + return_authorizations.first.should_not == return_authorizations.last + end + + it 'can control the page size through a parameter' do + order.return_authorizations << create(:return_authorization) + order.return_authorizations << create(:return_authorization) + api_get :index, :order_id => order.id, :per_page => 1 + json_response['count'].should == 1 + json_response['current_page'].should == 1 + json_response['pages'].should == 2 + end + + it 'can query the results through a paramter' do + order.return_authorizations << create(:return_authorization) + expected_result = create(:return_authorization, :reason => 'damaged') + order.return_authorizations << expected_result + api_get :index, :q => { :reason_cont => 'damage' } + json_response['count'].should == 1 + json_response['return_authorizations'].first['return_authorization']['reason'].should eq expected_result.reason + end + + it "can learn how to create a new return authorization" do + api_get :new + json_response["attributes"].should == ["id", "number", "state", "amount", "order_id", "reason", "created_at", "updated_at"] + required_attributes = json_response["required_attributes"] + required_attributes.should include("order") + end + + it "can update a return authorization on the order" do + order.return_authorizations << create(:return_authorization) + return_authorization = order.return_authorizations.first + api_put :update, :id => return_authorization.id, :return_authorization => { :amount => 19.99 } + response.status.should == 200 + json_response.should have_attributes(attributes) + end + + it "can delete a return authorization on the order" do + order.return_authorizations << create(:return_authorization) + return_authorization = order.return_authorizations.first + api_delete :destroy, :id => return_authorization.id + response.status.should == 204 + lambda { return_authorization.reload }.should raise_error(ActiveRecord::RecordNotFound) + end + + it "can add a new return authorization to an existing order" do + api_post :create, :return_autorization => { :order_id => order.id, :amount => 14.22, :reason => "Defective" } + response.status.should == 201 + json_response.should have_attributes(attributes) + json_response["return_authorization"]["state"].should_not be_blank + end + end + + context "as just another user" do + it "cannot add a return authorization to the order" do + api_post :create, :return_autorization => { :order_id => order.id, :amount => 14.22, :reason => "Defective" } + assert_unauthorized! + end + + it "cannot update a return authorization on the order" do + order.return_authorizations << create(:return_authorization) + return_authorization = order.return_authorizations.first + api_put :update, :id => return_authorization.id, :return_authorization => { :amount => 19.99 } + assert_unauthorized! + return_authorization.reload.amount.should_not == 19.99 + end + + it "cannot delete a return authorization on the order" do + order.return_authorizations << create(:return_authorization) + return_authorization = order.return_authorizations.first + api_delete :destroy, :id => return_authorization.id + assert_unauthorized! + lambda { return_authorization.reload }.should_not raise_error(ActiveRecord::RecordNotFound) + end + end + end +end diff --git a/api/spec/controllers/spree/api/v1/shipments_controller_spec.rb b/api/spec/controllers/spree/api/v1/shipments_controller_spec.rb index c5476b4b454..cc5886ba0ed 100644 --- a/api/spec/controllers/spree/api/v1/shipments_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/shipments_controller_spec.rb @@ -1,16 +1,31 @@ require 'spec_helper' describe Spree::Api::V1::ShipmentsController do + render_views let!(:shipment) { create(:shipment) } let!(:attributes) { [:id, :tracking, :number, :cost, :shipped_at] } before do - Spree::Order.any_instance.stub(:paid? => true) + Spree::Order.any_instance.stub(:paid? => true, :complete? => true) stub_authentication! end - context "working with a shipment" do - let!(:resource_scoping) { { :order_id => shipment.order.to_param, :id => shipment.to_param } } + let!(:resource_scoping) { { :order_id => shipment.order.to_param, :id => shipment.to_param } } + + context "as a non-admin" do + it "cannot make a shipment ready" do + api_put :ready + assert_unauthorized! + end + + it "cannot make a shipment shipped" do + api_put :ship + assert_unauthorized! + end + end + + context "as an admin" do + sign_in_as_admin! it "can make a shipment ready" do api_put :ready diff --git a/api/spec/controllers/spree/api/v1/taxonomies_controller_spec.rb b/api/spec/controllers/spree/api/v1/taxonomies_controller_spec.rb index 32fb2bed04e..73ab462b6d7 100644 --- a/api/spec/controllers/spree/api/v1/taxonomies_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/taxonomies_controller_spec.rb @@ -20,8 +20,23 @@ module Spree it "gets all taxonomies" do api_get :index - json_response.first['taxonomy']['name'].should eq taxonomy.name - json_response.first['taxonomy']['root']['taxons'].count.should eq 1 + json_response["taxonomies"].first['taxonomy']['name'].should eq taxonomy.name + json_response["taxonomies"].first['taxonomy']['root']['taxons'].count.should eq 1 + end + + it 'can control the page size through a parameter' do + create(:taxonomy) + api_get :index, :per_page => 1 + json_response['count'].should == 1 + json_response['current_page'].should == 1 + json_response['pages'].should == 2 + end + + it 'can query the results through a paramter' do + expected_result = create(:taxonomy, :name => 'Style') + api_get :index, :q => { :name_cont => 'style' } + json_response['count'].should == 1 + json_response['taxonomies'].first['taxonomy']['name'].should eq expected_result.name end it "gets a single taxonomy" do @@ -82,7 +97,11 @@ module Spree json_response["error"].should == "Invalid resource. Please fix errors and try again." errors = json_response["errors"] end - end + it "can destroy" do + api_delete :destroy, :id => taxonomy.id + response.status.should == 204 + end + end end end diff --git a/api/spec/controllers/spree/api/v1/taxons_controller_spec.rb b/api/spec/controllers/spree/api/v1/taxons_controller_spec.rb index bd7de8192db..82d7d7f6d71 100644 --- a/api/spec/controllers/spree/api/v1/taxons_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/taxons_controller_spec.rb @@ -4,9 +4,9 @@ module Spree describe Api::V1::TaxonsController do render_views - let(:taxonomy) { Factory(:taxonomy) } - let(:taxon) { Factory(:taxon, :name => "Ruby", :taxonomy => taxonomy) } - let(:taxon2) { Factory(:taxon, :name => "Rails", :taxonomy => taxonomy) } + let(:taxonomy) { create(:taxonomy) } + let(:taxon) { create(:taxon, :name => "Ruby", :taxonomy => taxonomy) } + let(:taxon2) { create(:taxon, :name => "Rails", :taxonomy => taxonomy) } let(:attributes) { ["id", "name", "permalink", "position", "parent_id", "taxonomy_id"] } before do @@ -76,6 +76,11 @@ module Spree taxon.reload.children.count.should eq 1 end + + it "can destroy" do + api_delete :destroy, :taxonomy_id => taxonomy.id, :id => taxon.id + response.status.should == 204 + end end end diff --git a/api/spec/controllers/spree/api/v1/unauthenticated_products_controller_spec.rb b/api/spec/controllers/spree/api/v1/unauthenticated_products_controller_spec.rb new file mode 100644 index 00000000000..0d3a10a9a6f --- /dev/null +++ b/api/spec/controllers/spree/api/v1/unauthenticated_products_controller_spec.rb @@ -0,0 +1,26 @@ +require 'shared_examples/protect_product_actions' +require 'spec_helper' + +module Spree + describe Spree::Api::V1::ProductsController do + render_views + + let!(:product) { create(:product) } + let(:attributes) { [:id, :name, :description, :price, :available_on, :permalink, :count_on_hand, :meta_description, :meta_keywords, :taxon_ids] } + + context "without authentication" do + before { Spree::Api::Config[:requires_authentication] = false } + + it "retreives a list of products" do + api_get :index + json_response["products"].first.should have_attributes(attributes) + json_response["count"].should == 1 + json_response["current_page"].should == 1 + json_response["pages"].should == 1 + end + + it_behaves_like "modifying product actions are restricted" + end + end +end + diff --git a/api/spec/controllers/spree/api/v1/variants_controller_spec.rb b/api/spec/controllers/spree/api/v1/variants_controller_spec.rb index 9b57a8fdff2..5d353c0e550 100644 --- a/api/spec/controllers/spree/api/v1/variants_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/variants_controller_spec.rb @@ -20,16 +20,68 @@ module Spree stub_authentication! end - it "can see a list of all variants" do + it "can see a paginated list of variants" do api_get :index - json_response.first.should have_attributes(attributes) - option_values = json_response.last["variant"]["option_values"] + json_response["variants"].first.should have_attributes(attributes) + json_response["count"].should == 1 + json_response["current_page"].should == 1 + json_response["pages"].should == 1 + end + + it 'can control the page size through a parameter' do + create(:variant) + api_get :index, :per_page => 1 + json_response['count'].should == 1 + json_response['current_page'].should == 1 + json_response['pages'].should == 3 + end + + it 'can query the results through a paramter' do + expected_result = create(:variant, :sku => 'FOOBAR') + api_get :index, :q => { :sku_cont => 'FOO' } + json_response['count'].should == 1 + json_response['variants'].first['variant']['sku'].should eq expected_result.sku + end + + it "variants returned contain option values data" do + api_get :index + option_values = json_response["variants"].last["variant"]["option_values"] option_values.first.should have_attributes([:name, :presentation, :option_type_name, :option_type_id]) end + # Regression test for #2141 + context "a deleted variant" do + before do + variant.update_column(:deleted_at, Time.now) + end + + it "is not returned in the results" do + api_get :index + json_response["variants"].count.should == 0 + end + + it "is not returned even when show_deleted is passed" do + api_get :index, :show_deleted => true + json_response["variants"].count.should == 0 + end + end + + context "pagination" do + default_per_page(1) + + it "can select the next page of variants" do + second_variant = create(:variant) + api_get :index, :page => 2 + json_response["variants"].first.should have_attributes(attributes) + json_response["total_count"].should == 3 + json_response["current_page"].should == 2 + json_response["pages"].should == 3 + end + end + it "can see a single variant" do api_get :show, :id => variant.to_param json_response.should have_attributes(attributes) @@ -66,6 +118,18 @@ module Spree sign_in_as_admin! let(:resource_scoping) { { :product_id => variant.product.to_param } } + # Test for #2141 + context "deleted variants" do + before do + variant.update_column(:deleted_at, Time.now) + end + + it "are visible by admin" do + api_get :index, :show_deleted => 1 + json_response["variants"].count.should == 1 + end + end + it "can create a new variant" do api_post :create, :variant => { :sku => "12345" } json_response.should have_attributes(attributes) @@ -81,7 +145,7 @@ module Spree it "can delete a variant" do api_delete :destroy, :id => variant.to_param - response.status.should == 200 + response.status.should == 204 lambda { variant.reload }.should raise_error(ActiveRecord::RecordNotFound) end end diff --git a/api/spec/controllers/spree/api/v1/zones_controller_spec.rb b/api/spec/controllers/spree/api/v1/zones_controller_spec.rb index 6b8b5572543..787054b8ed9 100644 --- a/api/spec/controllers/spree/api/v1/zones_controller_spec.rb +++ b/api/spec/controllers/spree/api/v1/zones_controller_spec.rb @@ -13,7 +13,22 @@ module Spree it "gets list of zones" do api_get :index - json_response.first.should have_attributes(attributes) + json_response['zones'].first.should have_attributes(attributes) + end + + it 'can control the page size through a parameter' do + create(:zone) + api_get :index, :per_page => 1 + json_response['count'].should == 1 + json_response['current_page'].should == 1 + json_response['pages'].should == 2 + end + + it 'can query the results through a paramter' do + expected_result = create(:zone, :name => 'South America') + api_get :index, :q => { :name_cont => 'south' } + json_response['count'].should == 1 + json_response['zones'].first['zone']['name'].should eq expected_result.name end it "gets a zone" do @@ -27,26 +42,46 @@ module Spree sign_in_as_admin! it "can create a new zone" do - api_post :create, :zone => { :name => "North Pole", - :zone_members => [ :zone_member => { - :zoneable_id => 1 }] } + params = { + :zone => { + :name => "North Pole", + :zone_members => [ + { + :zoneable_type => "Spree::Country", + :zoneable_id => 1 + } + ] + } + } + + api_post :create, params response.status.should == 201 json_response.should have_attributes(attributes) + json_response["zone"]["zone_members"].should_not be_empty end it "updates a zone" do - api_put :update, :id => @zone.id, - :zone => { :name => "Americas", - :zone_members => [ :zone_member => { - :zoneable_type => 'Spree::Country', - :zoneable_id => 1 }]} + params = { :id => @zone.id, + :zone => { + :name => "North Pole", + :zone_members => [ + { + :zoneable_type => "Spree::Country", + :zoneable_id => 1 + } + ] + } + } + + api_put :update, params response.status.should == 200 - json_response['zone']['name'].should eq 'Americas' + json_response['zone']['name'].should eq 'North Pole' + json_response['zone']['zone_members'].should_not be_blank end it "can delete a zone" do api_delete :destroy, :id => @zone.id - response.status.should == 200 + response.status.should == 204 lambda { @zone.reload }.should raise_error(ActiveRecord::RecordNotFound) end end diff --git a/api/spec/shared_examples/protect_product_actions.rb b/api/spec/shared_examples/protect_product_actions.rb new file mode 100644 index 00000000000..0dc3c2607d8 --- /dev/null +++ b/api/spec/shared_examples/protect_product_actions.rb @@ -0,0 +1,17 @@ +shared_examples "modifying product actions are restricted" do + it "cannot create a new product if not an admin" do + api_post :create, :product => { :name => "Brand new product!" } + assert_unauthorized! + end + + it "cannot update a product" do + api_put :update, :id => product.to_param, :product => { :name => "I hacked your store!" } + assert_unauthorized! + end + + it "cannot delete a product" do + api_delete :destroy, :id => product.to_param + assert_unauthorized! + end +end + diff --git a/api/spec/spec_helper.rb b/api/spec/spec_helper.rb index 01c364898c3..b2f1b6903ae 100644 --- a/api/spec/spec_helper.rb +++ b/api/spec/spec_helper.rb @@ -19,4 +19,9 @@ config.include FactoryGirl::Syntax::Methods config.include Spree::Api::TestingSupport::Helpers, :type => :controller config.extend Spree::Api::TestingSupport::Setup, :type => :controller + config.include Spree::Core::TestingSupport::Preferences, :type => :controller + + config.before do + Spree::Api::Config[:requires_authentication] = true + end end diff --git a/api/spree_api.gemspec b/api/spree_api.gemspec index 3d42a469866..3596f6ee847 100644 --- a/api/spree_api.gemspec +++ b/api/spree_api.gemspec @@ -16,7 +16,6 @@ Gem::Specification.new do |gem| gem.version = version gem.add_dependency 'spree_core', version - gem.add_dependency 'rabl', '0.6.5' gem.add_development_dependency 'rspec-rails', '2.9.0' gem.add_development_dependency 'database_cleaner' diff --git a/cmd/lib/spree_cmd.rb b/cmd/lib/spree_cmd.rb index 69c8eae1f73..ef3ea40140b 100644 --- a/cmd/lib/spree_cmd.rb +++ b/cmd/lib/spree_cmd.rb @@ -1,19 +1,15 @@ require 'thor' require 'thor/group' -module SpreeCmd - class Command - - if ARGV.first == 'version' - puts Gem.loaded_specs['spree_cmd'].version - elsif ARGV.first == 'extension' - ARGV.shift - require 'spree_cmd/extension' - SpreeCmd::Extension.start - else - ARGV.shift - require 'spree_cmd/installer' - SpreeCmd::Installer.start - end - end -end \ No newline at end of file +case ARGV.first + when 'version', '-v', '--version' + puts Gem.loaded_specs['spree_cmd'].version + when 'extension' + ARGV.shift + require 'spree_cmd/extension' + SpreeCmd::Extension.start + else + ARGV.shift + require 'spree_cmd/installer' + SpreeCmd::Installer.start +end diff --git a/cmd/lib/spree_cmd/extension.rb b/cmd/lib/spree_cmd/extension.rb index 2044dd0d88d..788b7009d65 100644 --- a/cmd/lib/spree_cmd/extension.rb +++ b/cmd/lib/spree_cmd/extension.rb @@ -51,7 +51,7 @@ def class_name end def spree_version - '1.2.0.rc1' + '1.2.2' end def use_prefix(prefix) diff --git a/cmd/lib/spree_cmd/installer.rb b/cmd/lib/spree_cmd/installer.rb index 6cd6fbea8e2..038c5c0ea5a 100644 --- a/cmd/lib/spree_cmd/installer.rb +++ b/cmd/lib/spree_cmd/installer.rb @@ -1,4 +1,5 @@ require 'rbconfig' +require 'active_support/core_ext/string' module SpreeCmd @@ -24,9 +25,6 @@ class Installer < Thor::Group class_option :branch, :type => :string, :desc => 'Spree gem git branch' class_option :tag, :type => :string, :desc => 'Spree gem git tag' - class_option :precompile_assets, :type => :boolean, :default => true, - :desc => 'Precompile spree assets to public/assets' - def verify_rails unless rails_project? say "#{@app_path} is not a rails project." @@ -56,14 +54,22 @@ def prepare_options elsif options[:version] @spree_gem_options[:version] = options[:version] else - require 'spree/core/version' - @spree_gem_options[:version] = Spree.version + version = Gem.loaded_specs['spree_cmd'].version + @spree_gem_options[:version] = version.to_s end end def ask_questions @install_default_gateways = ask_with_default('Would you like to install the default gateways?') @install_default_auth = ask_with_default('Would you like to install the default authentication system?') + unless @install_default_auth + @user_class = ask("What is the name of the class representing users within your application? [User]") + if @user_class.blank? + @user_class = "User" + end + else + @user_class = "Spree::User" + end if options[:skip_install_data] @run_migrations = false @@ -79,8 +85,6 @@ def ask_questions @load_sample_data = false end end - - @precompile_assets = options[:precompile_assets] && ask_with_default('Would you like to precompile assets?') end def add_gems @@ -94,7 +98,7 @@ def add_gems end if @install_default_auth - gem :spree_auth_devise, :git => "git://github.com/radar/spree_auth_devise" + gem :spree_auth_devise, :github => "spree/spree_auth_devise", :branch => "1-2-stable" end run 'bundle install', :capture => true @@ -106,6 +110,7 @@ def initialize_spree spree_options << "--migrate=#{@run_migrations}" spree_options << "--seed=#{@load_seed_data}" spree_options << "--sample=#{@load_sample_data}" + spree_options << "--user_class=#{@user_class}" spree_options << "--auto_accept" if options[:auto_accept] inside @app_path do @@ -113,15 +118,6 @@ def initialize_spree end end - def precompile_assets - if @precompile_assets - say_status :precompiling, 'assets' - inside @app_path do - run 'bundle exec rake assets:precompile', :verbose => false - end - end - end - private def gem(name, gem_options={}) @@ -181,10 +177,16 @@ def windows? end def image_magick_installed? + cmd = 'identify -version' + if RUBY_PLATFORM =~ /mingw|mswin/ #windows + cmd += " >nul" + else + cmd += " >/dev/null" + end # true if command executed succesfully # false for non zero exit status # nil if command execution fails - system('identify -version') + system(cmd) end end end diff --git a/cmd/lib/spree_cmd/templates/extension/Gemfile b/cmd/lib/spree_cmd/templates/extension/Gemfile index d65e2a66952..17dbc22025e 100644 --- a/cmd/lib/spree_cmd/templates/extension/Gemfile +++ b/cmd/lib/spree_cmd/templates/extension/Gemfile @@ -1,3 +1,6 @@ source 'http://rubygems.org' +# Provides basic authentication functionality for testing parts of your engine +gem 'spree_auth_devise', :git => "git://github.com/spree/spree_auth_devise" + gemspec diff --git a/cmd/lib/spree_cmd/templates/extension/gitignore b/cmd/lib/spree_cmd/templates/extension/gitignore index 2b6b0de6feb..e8a3f9c27e6 100644 --- a/cmd/lib/spree_cmd/templates/extension/gitignore +++ b/cmd/lib/spree_cmd/templates/extension/gitignore @@ -4,6 +4,7 @@ .DS_Store .idea .project +.sass-cache coverage Gemfile.lock tmp diff --git a/cmd/lib/spree_cmd/templates/extension/spec/spec_helper.rb.tt b/cmd/lib/spree_cmd/templates/extension/spec/spec_helper.rb.tt index 8d6355dee89..2bc8b0923d4 100644 --- a/cmd/lib/spree_cmd/templates/extension/spec/spec_helper.rb.tt +++ b/cmd/lib/spree_cmd/templates/extension/spec/spec_helper.rb.tt @@ -12,6 +12,9 @@ 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/testing_support/env' +require 'spree/core/testing_support/controller_requests' +require 'spree/core/testing_support/authorization_helpers' require 'spree/core/url_helpers' RSpec.configure do |config| diff --git a/common_spree_dependencies.rb b/common_spree_dependencies.rb index 259a631c20c..eb97ab1dc4b 100644 --- a/common_spree_dependencies.rb +++ b/common_spree_dependencies.rb @@ -1,4 +1,4 @@ -# By placing all of Spree's shared dependencies in this file and then loading +# By placing all of Spree's shared dependencies in this file and then loading # it for each component's Gemfile, we can be sure that we're only testing just # the one component of Spree. source 'http://rubygems.org' @@ -24,21 +24,13 @@ gem 'ffaker' gem 'shoulda-matchers', '~> 1.0.0' - gem 'capybara' - gem 'selenium-webdriver', '2.22.2' + gem 'capybara', '1.1.3' + gem 'selenium-webdriver', '2.25.0' gem 'database_cleaner', '0.7.1' gem 'launchy' # gem 'debugger' end -# platform :ruby_18 do -# gem "ruby-debug" -# end - -# platform :ruby_19 do -# gem "ruby-debug19" -# end - gemspec diff --git a/core/app/assets/images/noimage/large.png b/core/app/assets/images/noimage/large.png new file mode 100644 index 00000000000..1caf12fea9f Binary files /dev/null and b/core/app/assets/images/noimage/large.png differ diff --git a/core/app/assets/javascripts/admin/admin.js.erb b/core/app/assets/javascripts/admin/admin.js.erb index d2de82a07fa..c0a3223c9af 100644 --- a/core/app/assets/javascripts/admin/admin.js.erb +++ b/core/app/assets/javascripts/admin/admin.js.erb @@ -1,5 +1,7 @@ +//<%#encoding: UTF-8%> //= require_self //= require admin/product_autocomplete +//= require admin/taxon_autocomplete //= require select2 /** @@ -49,9 +51,9 @@ $.fn.radioControlsVisibilityOfElement = function(dependentElementSelector){ $.fn.objectPicker = function(url){ $(this).tokenInput(url + "&authenticity_token=" + escape(AUTH_TOKEN), { searchDelay : 600, - hintText : strings.type_to_search, - noResultsText : strings.no_results, - searchingText : strings.searching, + hintText : Spree.translations.type_to_search, + noResultsText : Spree.translations.no_results, + searchingText : Spree.translations.searching, prePopulateFromInput : true }); }; @@ -62,12 +64,12 @@ $.fn.productPicker = function(){ handle_date_picker_fields = function(){ $('.datepicker').datepicker({ - dateFormat: "<%= ::I18n.t(:format, :scope => 'spree.date_picker', :default => 'yy/mm/dd') %>", - dayNames: <%= ::I18n.t(:day_names, :scope => :date).to_json %>, - dayNamesMin: <%= ::I18n.t(:abbr_day_names, :scope => :date).to_json %>, - monthNames: <%= (::I18n.t(:month_names, :scope => :date).delete_if(&:blank?)).to_json %>, - prevText: '<%= ::I18n.t(:previous) %>', - nextText: '<%= ::I18n.t(:next) %>', + dateFormat: Spree.translations.date_picker, + dayNames: Spree.translations.abbr_day_names, + dayNamesMin: Spree.translations.abbr_day_names, + monthNames: Spree.translations.month_names, + prevText: Spree.translations.previous, + nextText: Spree.translations.next, showOn: "button", buttonImage: "<%= asset_path 'datepicker/cal.gif' %>", buttonImageOnly: true diff --git a/core/app/assets/javascripts/admin/checkouts/edit.js b/core/app/assets/javascripts/admin/checkouts/edit.js index 7324e8783b2..f89f72798a9 100644 --- a/core/app/assets/javascripts/admin/checkouts/edit.js +++ b/core/app/assets/javascripts/admin/checkouts/edit.js @@ -33,7 +33,7 @@ $(document).ready(function(){ } prep_user_autocomplete_data = function(data){ - return $.map(eval(data), function(row) { + return $.map(eval(data['users']), function(row) { return { data: row['user'], value: row['user']['email'], @@ -45,10 +45,10 @@ $(document).ready(function(){ if ($("#customer_search").length > 0) { $("#customer_search").autocomplete({ minChars: 5, - delay: 1500, + delay: 500, source: function(request, response) { var params = { q: $('#customer_search').val(), - authenticity_token: encodeURIComponent($('meta[name=csrf-token]').attr("content")) } + authenticity_token: AUTH_TOKEN } $.get(Spree.routes.user_search + '&' + jQuery.param(params), function(data) { result = prep_user_autocomplete_data(data) response(result); @@ -81,7 +81,7 @@ $(document).ready(function(){ $('#user_id').val(ui.item.data['id']); $('#guest_checkout_true').prop("checked", false); $('#guest_checkout_false').prop("checked", true); - $('#guest_checkout_false').prob("disabled", false); + $('#guest_checkout_false').prop("disabled", false); return true; } }).data("autocomplete")._renderItem = function(ul, item) { diff --git a/core/app/assets/javascripts/admin/product_autocomplete.js.erb b/core/app/assets/javascripts/admin/product_autocomplete.js.erb index 5fe05e762f4..1d2789d97aa 100644 --- a/core/app/assets/javascripts/admin/product_autocomplete.js.erb +++ b/core/app/assets/javascripts/admin/product_autocomplete.js.erb @@ -1,3 +1,4 @@ +<%#encoding: UTF-8%> // Product autocompletion image_html = function(item){ return ""; @@ -15,9 +16,9 @@ format_product_autocomplete = function(item){ html += "

" + product['name'] + "

"; if (product['master']) { - html += "<%= ::I18n.t(:sku) %>: " + product['master']['sku'] + ""; + html += "" + Spree.translations.sku + ":" + product['master']['sku'] + ""; } - html += "<%= ::I18n.t(:on_hand) %>: " + product['count_on_hand'] + "
"; + html += "" + Spree.translations.on_hand + ":" + product['count_on_hand'] + ""; }else{ // variant var variant = item.data['variant']; @@ -36,8 +37,8 @@ format_product_autocomplete = function(item){ }).join(", ") html += "

" + name + "

"; - html += "<%= ::I18n.t(:sku) %>: " + variant['sku'] + ""; - html += "<%= ::I18n.t(:on_hand) %>: " + variant['count_on_hand'] + "
"; + html += "" + Spree.translations.sku + ":" + variant['sku'] + ""; + html += "" + Spree.translations.on_hand + ":" + variant['count_on_hand'] + ""; } return html diff --git a/core/app/assets/javascripts/admin/shipping_methods.js b/core/app/assets/javascripts/admin/shipping_methods.js deleted file mode 100644 index 75fb201afe7..00000000000 --- a/core/app/assets/javascripts/admin/shipping_methods.js +++ /dev/null @@ -1,15 +0,0 @@ -$(document).ready(function() { - if ($(".categories input:checked").length > 0) { - $('input[type=checkbox]:not(:checked)').attr('disabled', true); - } - - categoryCheckboxes = '.categories input[type=checkbox]'; - $(categoryCheckboxes).change(function(){ - if($(this).is(':checked')) { - $(categoryCheckboxes + ':not(:checked)').attr('disabled', true); - $(this).removeAttr('disabled'); - } else { - $('input[type=checkbox]').removeAttr('disabled'); - } - }); -}); diff --git a/core/app/assets/javascripts/admin/shipping_methods.js.coffee b/core/app/assets/javascripts/admin/shipping_methods.js.coffee new file mode 100644 index 00000000000..c6b63332087 --- /dev/null +++ b/core/app/assets/javascripts/admin/shipping_methods.js.coffee @@ -0,0 +1,10 @@ +$ -> + ($ 'input[type=checkbox]:not(:checked)').attr 'disabled', true if ($ '.categories input:checked').length > 0 + categoryCheckboxes = '.categories input[type=checkbox]' + $(categoryCheckboxes).change -> + if ($ this).is(':checked') + ($ categoryCheckboxes + ':not(:checked)').attr 'disabled', true + ($ this).removeAttr 'disabled' + else + ($ 'input[type=checkbox]').removeAttr 'disabled' + diff --git a/core/app/assets/javascripts/admin/taxon_autocomplete.js.erb b/core/app/assets/javascripts/admin/taxon_autocomplete.js.erb new file mode 100644 index 00000000000..aba94328cdf --- /dev/null +++ b/core/app/assets/javascripts/admin/taxon_autocomplete.js.erb @@ -0,0 +1,34 @@ +function cleanTaxons(data) { + var taxons = $.map(data['taxons'], function(result) { + return result['taxon'] + }) + return taxons; +} + +$(document).ready(function() { + $("#product_taxon_ids").select2({ + placeholder: "Add a taxon", + multiple: true, + initSelection: function(element, callback) { + return $.getJSON(Spree.routes.taxon_search + "?ids=" + (element.val()), null, function(data) { + return callback(self.cleanTaxons(data)); + }) + }, + ajax: { + url: Spree.routes.taxon_search, + datatype: 'json', + data: function(term, page) { + return { q: term } + }, + results: function (data, page) { + return { results: self.cleanTaxons(data) } + } + }, + formatResult: function(taxon) { + return taxon.pretty_name + }, + formatSelection: function(taxon) { + return taxon.pretty_name + } + }) +}) diff --git a/core/app/assets/javascripts/admin/taxonomy.js b/core/app/assets/javascripts/admin/taxonomy.js index 2ff22301b07..c9d47b62b4b 100644 --- a/core/app/assets/javascripts/admin/taxonomy.js +++ b/core/app/assets/javascripts/admin/taxonomy.js @@ -59,7 +59,7 @@ var handle_delete = function(e, data){ last_rollback = data.rlbk; var node = data.rslt.obj; - jConfirm('Are you sure you want to delete this taxon?', 'Confirm Taxon Deletion', function(r) { + jConfirm(Spree.translations.are_you_sure_delete, Spree.translations.confirm_delete, function(r) { if(r){ $.ajax({ type: "POST", @@ -103,7 +103,7 @@ $(document).ready(function(){ }, "strings" : { "new_node" : new_taxon, - "loading" : loading + "..." + "loading" : Spree.translations.loading + "..." }, "crrm" : { "move" : { @@ -131,48 +131,48 @@ $(document).ready(function(){ if(type_of_node == "root") { menu = { "create" : { - "label" : "Create", + "label" : Spree.translations.add, "action" : function (obj) { this.create(obj); } }, "paste" : { "separator_before" : true, - "label" : "Paste", + "label" : Spree.translations.paste, "action" : function (obj) { is_cut = false; this.paste(obj); }, "_disabled" : is_cut == false }, "edit" : { "separator_before" : true, - "label" : "Edit", + "label" : Spree.translations.edit, "action" : function (obj) { window.location = base_url + obj.attr("id") + "/edit/"; } } } } else { menu = { "create" : { - "label" : "Create", + "label" : Spree.translations.add, "action" : function (obj) { this.create(obj); } }, "rename" : { - "label" : "Rename", + "label" : Spree.translations.rename, "action" : function (obj) { this.rename(obj); } }, "remove" : { - "label" : "Remove", + "label" : Spree.translations.remove, "action" : function (obj) { this.remove(obj); } }, "cut" : { "separator_before" : true, - "label" : "Cut", + "label" : Spree.translations.cut, "action" : function (obj) { is_cut = true; this.cut(obj); } }, "paste" : { - "label" : "Paste", + "label" : Spree.translations.paste, "action" : function (obj) { is_cut = false; this.paste(obj); }, "_disabled" : is_cut == false }, "edit" : { "separator_before" : true, - "label" : "Edit", + "label" : Spree.translations.edit, "action" : function (obj) { window.location = base_url + obj.attr("id") + "/edit/"; } } } diff --git a/core/app/assets/javascripts/admin/zone.js b/core/app/assets/javascripts/admin/zone.js deleted file mode 100644 index 1249ad586b1..00000000000 --- a/core/app/assets/javascripts/admin/zone.js +++ /dev/null @@ -1,29 +0,0 @@ -$(function() { - if ($('#country_based').is(':checked')) { - show_country(); - } else { - show_state(); - } - $('#country_based').click(function() { show_country();} ); - $('#state_based').click(function() { show_state();} ); -}) - -var show_country = function() { - $('#state_members :input').each(function() { $(this).prop("disabled", true); }) - $('#state_members').hide(); - $('#zone_members :input').each(function() { $(this).prop("disabled", true); }) - $('#zone_members').hide(); - $('#country_members :input').each(function() { $(this).prop("disabled", false); }) - $('#country_members').show(); -}; - -var show_state = function() { - $('#country_members :input').each(function() { $(this).prop("disabled", true); - }) - $('#country_members').hide(); - $('#zone_members :input').each(function() { $(this).prop("disabled", true); - }) - $('#zone_members').hide(); - $('#state_members :input').each(function() { $(this).prop("disabled", false); }) - $('#state_members').show(); -}; diff --git a/core/app/assets/javascripts/admin/zone.js.coffee b/core/app/assets/javascripts/admin/zone.js.coffee new file mode 100644 index 00000000000..2c70c78828a --- /dev/null +++ b/core/app/assets/javascripts/admin/zone.js.coffee @@ -0,0 +1,39 @@ +$ -> + if ($ '#country_based').is(':checked') + show_country() + else + show_state() + ($ '#country_based').click -> + show_country() + + ($ '#state_based').click -> + show_state() + + +show_country = -> + ($ '#state_members :input').each -> + ($ this).prop 'disabled', true + + ($ '#state_members').hide() + ($ '#zone_members :input').each -> + ($ this).prop 'disabled', true + + ($ '#zone_members').hide() + ($ '#country_members :input').each -> + ($ this).prop 'disabled', false + + ($ '#country_members').show() + +show_state = -> + ($ '#country_members :input').each -> + ($ this).prop 'disabled', true + + ($ '#country_members').hide() + ($ '#zone_members :input').each -> + ($ this).prop 'disabled', true + + ($ '#zone_members').hide() + ($ '#state_members :input').each -> + ($ this).prop 'disabled', false + + ($ '#state_members').show() \ No newline at end of file diff --git a/core/app/assets/javascripts/store/cart.js.coffee b/core/app/assets/javascripts/store/cart.js.coffee index ba6aef320ba..e220f7d362d 100644 --- a/core/app/assets/javascripts/store/cart.js.coffee +++ b/core/app/assets/javascripts/store/cart.js.coffee @@ -1,7 +1,9 @@ $ -> if ($ 'form#update-cart').is('*') - ($ 'form#update-cart a.delete').show().on 'click', (e) -> - $(this).parents('.line-item').first().find('input.line_item_quantity').val 0 - $(this).parents('form').first().submit() - e.preventDefault() - return \ No newline at end of file + ($ 'form#update-cart a.delete').show().one 'click', -> + ($ this).parents('.line-item').first().find('input.line_item_quantity').val 0 + ($ this).parents('form').first().submit() + false + + ($ 'form#update-cart').submit -> + ($ 'form#update-cart #update-button').attr('disabled', true) \ No newline at end of file diff --git a/core/app/assets/javascripts/store/checkout.js.coffee b/core/app/assets/javascripts/store/checkout.js.coffee index 6e0e8ad55e7..f0336cd8fcb 100644 --- a/core/app/assets/javascripts/store/checkout.js.coffee +++ b/core/app/assets/javascripts/store/checkout.js.coffee @@ -1,5 +1,5 @@ -disableSaveOnClick = -> - ($ 'form.edit_spree_order').submit -> +@disableSaveOnClick = -> + ($ 'form.edit_order').submit -> ($ this).find(':submit, :image').attr('disabled', true).removeClass('primary').addClass 'disabled' $ -> @@ -14,7 +14,7 @@ $ -> state_select = ($ 'p#' + region + 'state select') state_input = ($ 'p#' + region + 'state input') if states - selected = state_select.val() + selected = parseInt state_select.val() state_select.html '' states_with_blank = [ [ '', '' ] ].concat(states) $.each states_with_blank, (pos, id_nm) -> @@ -51,7 +51,11 @@ $ -> ).triggerHandler 'click' if ($ '#checkout_form_payment').is('*') + # Activate already checked payment method if form is re-rendered + # i.e. if user enters invalid data + ($ 'input[type="radio"]:checked').click() + ($ 'input[type="radio"][name="order[payments_attributes][][payment_method_id]"]').click(-> ($ '#payment-methods li').hide() ($ '#payment_method_' + @value).show() if @checked - ).triggerHandler 'click' \ No newline at end of file + ) diff --git a/core/app/assets/javascripts/store/product.js.coffee b/core/app/assets/javascripts/store/product.js.coffee index 6b60e5cb6c1..1960888a949 100644 --- a/core/app/assets/javascripts/store/product.js.coffee +++ b/core/app/assets/javascripts/store/product.js.coffee @@ -7,7 +7,6 @@ add_image_handlers = -> ($ this).mouseout -> ($ 'ul.thumbnails li').removeClass 'selected' ($ event.currentTarget).parent('li').addClass 'selected' - false ($ 'ul.thumbnails li').on 'mouseenter', (event) -> @@ -31,6 +30,6 @@ show_variant_images = (variant_id) -> $ -> add_image_handlers() - show_variant_images ($ '#product-variants input[type="radio"]').eq(0).attr('value') if ($ '#product-variants input[type=radio]').length > 0 + show_variant_images ($ '#product-variants input[type="radio"]').eq(0).attr('value') if ($ '#product-variants input[type="radio"]').length > 0 ($ '#product-variants input[type="radio"]').click (event) -> show_variant_images @value \ No newline at end of file diff --git a/core/app/assets/stylesheets/admin/admin-form.css.erb b/core/app/assets/stylesheets/admin/admin-form.css.erb index f2ebac9030d..f207825e1f3 100755 --- a/core/app/assets/stylesheets/admin/admin-form.css.erb +++ b/core/app/assets/stylesheets/admin/admin-form.css.erb @@ -161,3 +161,7 @@ fieldset { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; } +/* make sure the select2 boxes on product#edit have some width to select. */ +.select2-choices { + min-width: 150px; +} diff --git a/core/app/assets/stylesheets/store/screen.css.scss b/core/app/assets/stylesheets/store/screen.css.scss index cf250d70cb8..aa69a6d0e19 100644 --- a/core/app/assets/stylesheets/store/screen.css.scss +++ b/core/app/assets/stylesheets/store/screen.css.scss @@ -1,4 +1,4 @@ -@import "store/variables"; +@import "./variables.css.scss"; /*--------------------------------------*/ /* Basic styles diff --git a/core/app/controllers/spree/admin/banners_controller.rb b/core/app/controllers/spree/admin/banners_controller.rb index b659b2b6f47..2c33b71cf7d 100644 --- a/core/app/controllers/spree/admin/banners_controller.rb +++ b/core/app/controllers/spree/admin/banners_controller.rb @@ -2,12 +2,12 @@ module Spree module Admin class BannersController < Spree::Admin::BaseController def dismiss - if request.xhr? and params[:id] + if params[:id] if user = try_spree_current_user user.dismiss_banner(params[:id]) end - render :nothing => true end + render :nothing => true end end end diff --git a/core/app/controllers/spree/admin/general_settings_controller.rb b/core/app/controllers/spree/admin/general_settings_controller.rb index 41b90f97b3d..f9d9811baf4 100644 --- a/core/app/controllers/spree/admin/general_settings_controller.rb +++ b/core/app/controllers/spree/admin/general_settings_controller.rb @@ -10,7 +10,7 @@ def edit @preferences = [:site_name, :default_seo_title, :default_meta_keywords, :default_meta_description, :site_url, :allow_ssl_in_production, :allow_ssl_in_staging, :allow_ssl_in_development_and_test, - :check_for_spree_alerts] + :check_for_spree_alerts, :display_currency] end def update diff --git a/core/app/controllers/spree/admin/images_controller.rb b/core/app/controllers/spree/admin/images_controller.rb index 0d08d68b441..01fc66b2c16 100644 --- a/core/app/controllers/spree/admin/images_controller.rb +++ b/core/app/controllers/spree/admin/images_controller.rb @@ -19,30 +19,22 @@ def update_positions private - + def location_after_save admin_product_images_url(@product) end def load_data - @product = Product.find_by_permalink(params[:product_id]) + @product = Product.where(:permalink => params[:product_id]).first @variants = @product.variants.collect do |variant| [variant.options_text, variant.id] end - @variants.insert(0, [I18n.t(:all), 'All']) + @variants.insert(0, [I18n.t(:all), @product.master.id]) end def set_viewable - if params[:image].has_key? :viewable_id - if params[:image][:viewable_id] == 'All' - @image.viewable = @product.master - else - @image.viewable_type = 'Spree::Variant' - @image.viewable_id = params[:image][:viewable_id] - end - else - @image.viewable = @product.master - end + @image.viewable_type = 'Spree::Variant' + @image.viewable_id = params[:image][:viewable_id] end def destroy_before diff --git a/core/app/controllers/spree/admin/mail_methods_controller.rb b/core/app/controllers/spree/admin/mail_methods_controller.rb index 7cfa7fcbec2..f76b433c3f6 100644 --- a/core/app/controllers/spree/admin/mail_methods_controller.rb +++ b/core/app/controllers/spree/admin/mail_methods_controller.rb @@ -3,6 +3,13 @@ module Admin class MailMethodsController < ResourceController after_filter :initialize_mail_settings + def update + if params[:mail_method][:preferred_smtp_password].blank? + params[:mail_method].delete(:preferred_smtp_password) + end + super + end + def testmail @mail_method = Spree::MailMethod.find(params[:id]) if TestMailer.test_email(@mail_method, try_spree_current_user).deliver diff --git a/core/app/controllers/spree/admin/orders_controller.rb b/core/app/controllers/spree/admin/orders_controller.rb index 295e696a6fe..aa4a712b4be 100644 --- a/core/app/controllers/spree/admin/orders_controller.rb +++ b/core/app/controllers/spree/admin/orders_controller.rb @@ -20,6 +20,8 @@ def index created_at_gt = params[:q][:created_at_gt] created_at_lt = params[:q][:created_at_lt] + params[:q].delete(:inventory_units_shipment_id_null) if params[:q][:inventory_units_shipment_id_null] == "0" + if !params[:q][:created_at_gt].blank? params[:q][:created_at_gt] = Time.zone.parse(params[:q][:created_at_gt]).beginning_of_day rescue "" end @@ -34,7 +36,9 @@ def index end @search = Order.ransack(params[:q]) - @orders = @search.result.includes([:user, :shipments, :payments]).page(params[:page]).per(Spree::Config[:orders_per_page]) + @orders = @search.result.includes([:user, :shipments, :payments]). + page(params[:page]). + per(params[:per_page] || Spree::Config[:orders_per_page]) # Restore dates params[:q][:created_at_gt] = created_at_gt diff --git a/core/app/controllers/spree/admin/payment_methods_controller.rb b/core/app/controllers/spree/admin/payment_methods_controller.rb index 9109868ecb3..c8afcdb5097 100644 --- a/core/app/controllers/spree/admin/payment_methods_controller.rb +++ b/core/app/controllers/spree/admin/payment_methods_controller.rb @@ -27,7 +27,13 @@ def update end payment_method_params = params[ActiveModel::Naming.param_key(@payment_method)] || {} - if @payment_method.update_attributes(params[:payment_method].merge(payment_method_params)) + attributes = params[:payment_method].merge(payment_method_params) + attributes.each do |k,v| + if k.include?("password") && attributes[k].blank? + attributes.delete(k) + end + end + if @payment_method.update_attributes(attributes) invoke_callbacks(:update, :after) flash.notice = I18n.t(:successfully_updated, :resource => I18n.t(:payment_method)) respond_with(@payment_method, :location => edit_admin_payment_method_path(@payment_method)) diff --git a/core/app/controllers/spree/admin/products_controller.rb b/core/app/controllers/spree/admin/products_controller.rb index fc500601555..52046384c73 100644 --- a/core/app/controllers/spree/admin/products_controller.rb +++ b/core/app/controllers/spree/admin/products_controller.rb @@ -19,13 +19,16 @@ def index end end - # override the destory method to set deleted_at value - # instead of actually deleting the product. - def destroy - @product = Product.find_by_permalink!(params[:id]) - @product.update_column(:deleted_at, Time.now) + def update + if params[:product][:taxon_ids].present? + params[:product][:taxon_ids] = params[:product][:taxon_ids].split(',') + end + super + end - @product.variants_including_master.update_all(:deleted_at => Time.now) + def destroy + @product = Product.where(:permalink => params[:id]).first! + @product.delete flash.notice = I18n.t('notice_messages.product_deleted') @@ -93,12 +96,10 @@ def collection page(params[:page]). per(Spree::Config[:admin_products_per_page]) - if params[:q][:s].include?("master_price") - # By applying the group in the main query we get an undefined method gsub for Arel::Nodes::Descending - # It seems to only work when the price is actually being sorted in the query - # To be investigated later. - @collection = @collection.group("spree_variants.price") - end + # PostgreSQL compatibility + if params[:q][:s].include?("master_price") + @collection = @collection.group("spree_variants.price") + end else includes = [{:variants => [:images, {:option_values => :option_type}]}, {:master => :images}] diff --git a/core/app/controllers/spree/admin/prototypes_controller.rb b/core/app/controllers/spree/admin/prototypes_controller.rb index 8bb14102235..2cf9d4e9f4e 100644 --- a/core/app/controllers/spree/admin/prototypes_controller.rb +++ b/core/app/controllers/spree/admin/prototypes_controller.rb @@ -29,8 +29,8 @@ def select private def set_habtm_associations - @prototype.property_ids = params[:property][:id] if params[:property] - @prototype.option_type_ids = params[:option_type][:id] if params[:option_type] + @prototype.property_ids = params[:property].blank? ? [] : params[:property][:id] + @prototype.option_type_ids = params[:option_type].blank? ? [] : params[:option_type][:id] end end end diff --git a/core/app/controllers/spree/admin/search_controller.rb b/core/app/controllers/spree/admin/search_controller.rb index f29f876aa4b..49abde0ef13 100644 --- a/core/app/controllers/spree/admin/search_controller.rb +++ b/core/app/controllers/spree/admin/search_controller.rb @@ -8,37 +8,17 @@ class SearchController < Spree::Admin::BaseController # TODO: Clean this up by moving searching out to user_class_extensions # And then JSON building with something like Active Model Serializers def users - @users = Spree.user_class.ransack({ - :m => 'or', - :email_start => params[:q], - :ship_address_firstname_start => params[:q], - :ship_address_lastname_start => params[:q], - :bill_address_firstname_start => params[:q], - :bill_address_lastname_start => params[:q] - }).result.limit(params[:limit] || 100) - - respond_with(@users) do |format| - format.json do - address_fields = [:firstname, :lastname, - :address1, :address2, - :city, :zipcode, - :phone, :state_name, - :state_id, :country_id] - includes = { - :only => address_fields, - :include => { - :state => { :only => :name }, - :country => { :only => :name } - } - } - - json = @users.to_json({ - :only => [:id, :email], - :include => { :bill_address => includes, :ship_address => includes } - }) - - render :json => json - end + if params[:ids] + @users = Spree.user_class.where(:id => params[:ids].split(',')) + else + @users = Spree.user_class.ransack({ + :m => 'or', + :email_start => params[:q], + :ship_address_firstname_start => params[:q], + :ship_address_lastname_start => params[:q], + :bill_address_firstname_start => params[:q], + :bill_address_lastname_start => params[:q] + }).result.limit(10) end end end diff --git a/core/app/controllers/spree/admin/taxons_controller.rb b/core/app/controllers/spree/admin/taxons_controller.rb index ef3826ca31a..18a4fb3a59a 100644 --- a/core/app/controllers/spree/admin/taxons_controller.rb +++ b/core/app/controllers/spree/admin/taxons_controller.rb @@ -4,6 +4,14 @@ class TaxonsController < Spree::Admin::BaseController respond_to :html, :json, :js + def search + if params[:ids] + @taxons = Spree::Taxon.where(:id => params[:ids].split(',')) + else + @taxons = Spree::Taxon.limit(20).search(:name_cont => params[:q]).result + end + end + def create @taxonomy = Taxonomy.find(params[:taxonomy_id]) @taxon = @taxonomy.taxons.build(params[:taxon]) diff --git a/core/app/controllers/spree/checkout_controller.rb b/core/app/controllers/spree/checkout_controller.rb index 148e09cf2e5..8b84a821be5 100644 --- a/core/app/controllers/spree/checkout_controller.rb +++ b/core/app/controllers/spree/checkout_controller.rb @@ -2,7 +2,12 @@ module Spree # Handles checkout logic. This is somewhat contrary to standard REST convention since there is not actually a # Checkout object. There's enough distinct logic specific to checkout which has nothing to do with updating an # order that this approach is waranted. - class CheckoutController < BaseController + # + # Much of this file, especially the update action is overriden in the promo gem. + # This is to allow for the promo behavior but also allow the promo gem to be + # removed if the functionality is not needed. + + class CheckoutController < Spree::BaseController ssl_required before_filter :load_order @@ -13,6 +18,7 @@ class CheckoutController < BaseController respond_to :html # Updates the order and advances to the next state (when possible.) + # Overriden by the promo gem if it exists. def update if @order.update_attributes(object_params) fire_event('spree.checkout.update') @@ -39,14 +45,21 @@ def update private def ensure_valid_state - if params[:state] && params[:state] != 'cart' && - !@order.checkout_steps.include?(params[:state]) - params[:state] == 'cart' - @order.state = 'cart' - redirect_to checkout_path + unless skip_state_validation? + if (params[:state] && !@order.checkout_steps.include?(params[:state])) || + (!params[:state] && !@order.checkout_steps.include?(@order.state)) + @order.state = 'cart' + redirect_to checkout_state_path(@order.checkout_steps.first) + end end end + # Should be overriden if you have areas of your checkout that don't match + # up to a step within checkout_steps, such as a registration step + def skip_state_validation? + false + end + def load_order @order = current_order redirect_to cart_path and return unless @order and @order.checkout_allowed? @@ -56,24 +69,6 @@ def load_order state_callback(:before) end - def associate_user - if try_spree_current_user && @order - if @order.user.blank? || @order.email.blank? - @order.associate_user!(try_spree_current_user) - end - end - - # This will trigger any "first order" promotions to be triggered - # Assuming of course that this session variable was set correctly in - # the authentication provider's registrations controller - if session[:spree_user_signup] - fire_event('spree.user.signup', :user => try_spree_current_user, :order => current_order(true)) - end - - session[:guest_token] = nil - session[:spree_user_signup] = nil - end - # Provides a route to redirect after order completion def completion_route order_path(@order) diff --git a/core/app/controllers/spree/home_controller.rb b/core/app/controllers/spree/home_controller.rb index 26500eee353..5c1dd586f73 100644 --- a/core/app/controllers/spree/home_controller.rb +++ b/core/app/controllers/spree/home_controller.rb @@ -5,6 +5,7 @@ class HomeController < BaseController def index @searcher = Spree::Config.searcher_class.new(params) + @searcher.current_user = try_spree_current_user @products = @searcher.retrieve_products respond_with(@products) end diff --git a/core/app/controllers/spree/orders_controller.rb b/core/app/controllers/spree/orders_controller.rb index 2ff308100ac..79e4a95f28a 100644 --- a/core/app/controllers/spree/orders_controller.rb +++ b/core/app/controllers/spree/orders_controller.rb @@ -1,5 +1,7 @@ module Spree class OrdersController < BaseController + ssl_required :show + rescue_from ActiveRecord::RecordNotFound, :with => :render_404 helper 'spree/products' @@ -13,9 +15,18 @@ def show def update @order = current_order if @order.update_attributes(params[:order]) - @order.line_items = @order.line_items.select {|li| li.quantity > 0 } + @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 } } + respond_with(@order) do |format| + format.html do + if params.has_key?(:checkout) + @order.next_transition.run_callbacks + redirect_to checkout_state_path(@order.checkout_steps.first) + else + redirect_to cart_path + end + end + end else respond_with(@order) end @@ -24,6 +35,7 @@ def update # Shows the current incomplete order from the session def edit @order = current_order(true) + associate_user end # Adds a new item to the order (creating a new order if none already exists) @@ -63,8 +75,10 @@ def empty redirect_to spree.cart_path end - def accurate_titles - @order && @order.completed? ? "#{Order.model_name.human} #{@order.number}" : t(:shopping_cart) - end + private + + def accurate_title + @order && @order.completed? ? "#{Order.model_name.human} #{@order.number}" : t(:shopping_cart) + end end end diff --git a/core/app/controllers/spree/products_controller.rb b/core/app/controllers/spree/products_controller.rb index dc39576fa2a..fa8cd136600 100644 --- a/core/app/controllers/spree/products_controller.rb +++ b/core/app/controllers/spree/products_controller.rb @@ -8,6 +8,7 @@ class ProductsController < BaseController def index @searcher = Config.searcher_class.new(params) + @searcher.current_user = try_spree_current_user @products = @searcher.retrieve_products respond_with(@products) end diff --git a/core/app/controllers/spree/taxons_controller.rb b/core/app/controllers/spree/taxons_controller.rb index a1ad043231b..39d7055ab0a 100644 --- a/core/app/controllers/spree/taxons_controller.rb +++ b/core/app/controllers/spree/taxons_controller.rb @@ -10,6 +10,7 @@ def show return unless @taxon @searcher = Spree::Config.searcher_class.new(params.merge(:taxon => @taxon.id)) + @searcher.current_user = try_spree_current_user @products = @searcher.retrieve_products respond_with(@taxon) diff --git a/core/app/helpers/spree/admin/general_settings_helper.rb b/core/app/helpers/spree/admin/general_settings_helper.rb new file mode 100644 index 00000000000..733c5be2547 --- /dev/null +++ b/core/app/helpers/spree/admin/general_settings_helper.rb @@ -0,0 +1,13 @@ +module Spree + module Admin + module GeneralSettingsHelper + def currency_options + currencies = ::Money::Currency.table.map do |code, details| + iso = details[:iso_code] + [iso, "#{details[:name]} (#{iso})"] + end + options_from_collection_for_select(currencies, :first, :last, Spree::Config[:currency]) + end + end + end +end diff --git a/core/app/helpers/spree/admin/users_helper.rb b/core/app/helpers/spree/admin/users_helper.rb index de30fbd901a..d656945443a 100644 --- a/core/app/helpers/spree/admin/users_helper.rb +++ b/core/app/helpers/spree/admin/users_helper.rb @@ -3,7 +3,7 @@ module Admin module UsersHelper def list_roles(user) # while testing spree-core itself user model does not have method roles - user.respond_to?(:roles) ? user.roles.collect { |role| role.name }.join(", ") : [] + user.respond_to?(:spree_roles) ? user.spree_roles.collect { |role| role.name }.join(", ") : [] end end end diff --git a/core/app/helpers/spree/base_helper.rb b/core/app/helpers/spree/base_helper.rb index 9100b53c30d..b6cd6bbaaab 100644 --- a/core/app/helpers/spree/base_helper.rb +++ b/core/app/helpers/spree/base_helper.rb @@ -22,22 +22,13 @@ def link_to_cart(text = nil) text = "#{text}: (#{t('empty')})" css_class = 'empty' else - text = "#{text}: (#{current_order.item_count}) #{order_subtotal(current_order)}".html_safe + text = "#{text}: (#{current_order.item_count}) #{current_order.display_total}".html_safe css_class = 'full' end link_to text, cart_path, :class => css_class end - def order_subtotal(order, options={}) - options.assert_valid_keys(:format_as_currency, :show_vat_text) - options.reverse_merge! :format_as_currency => true, :show_vat_text => true - - amount = order.total - - options.delete(:format_as_currency) ? number_to_currency(amount) : amount - end - def todays_short_date utc_to_local(Time.now.utc).to_ordinalized_s(:stub) end @@ -95,9 +86,13 @@ def logo(image_path=Spree::Config[:logo]) link_to image_tag(image_path), root_path end - def flash_messages + def flash_messages(opts = {}) + opts[:ignore_types] = [:commerce_tracking].concat(Array(opts[:ignore_types]) || []) + flash.each do |msg_type, text| - concat(content_tag :div, text, :class => "flash #{msg_type}") unless msg_type == :commerce_tracking + unless opts[:ignore_types].include?(msg_type) + concat(content_tag :div, text, :class => "flash #{msg_type}") + end end nil end @@ -145,16 +140,6 @@ def available_countries end.sort { |a, b| a.name <=> b.name } end - def format_price(price, options={}) - options.assert_valid_keys(:show_vat_text) - formatted_price = number_to_currency price - if options[:show_vat_text] - I18n.t(:price_with_vat_included, :price => formatted_price) - else - formatted_price - end - end - # generates nested url to product based on supplied taxon def seo_url(taxon, product = nil) return spree.nested_taxons_path(taxon.permalink) if product.nil? @@ -179,6 +164,10 @@ def gem_available?(name) Gem.available?(name) end + def money(amount) + Spree::Money.new(amount) + end + def method_missing(method_name, *args, &block) if image_style = image_style_from_method_name(method_name) define_image_method(image_style) diff --git a/core/app/helpers/spree/products_helper.rb b/core/app/helpers/spree/products_helper.rb index a2bf7e3c413..24b281e9d3c 100644 --- a/core/app/helpers/spree/products_helper.rb +++ b/core/app/helpers/spree/products_helper.rb @@ -6,9 +6,9 @@ def variant_price_diff(variant) diff = variant.price - variant.product.price return nil if diff == 0 if diff > 0 - "(#{t(:add)}: #{number_to_currency diff.abs})" + "(#{t(:add)}: #{Spree::Money.new(diff.abs)})" else - "(#{t(:subtract)}: #{number_to_currency diff.abs})" + "(#{t(:subtract)}: #{Spree::Money.new(diff.abs)})" end end @@ -17,10 +17,6 @@ def product_description(product) raw(product.description.gsub(/(.*?)\r?\n\r?\n/m, '

\1

')) end - def variant_images_hash(product) - product.variant_images.inject({}) { |h, img| (h[img.viewable_id] ||= []) << img; h } - end - def line_item_description(variant) description = variant.product.description if description.present? diff --git a/core/app/models/spree/ability.rb b/core/app/models/spree/ability.rb index 3b8ff4e49e3..a6864a7fb55 100644 --- a/core/app/models/spree/ability.rb +++ b/core/app/models/spree/ability.rb @@ -36,13 +36,13 @@ def initialize(user) can :manage, :all else ############################# - can :read, LegacyUser do |resource| + can :read, Spree.user_class do |resource| resource == user end - can :update, LegacyUser do |resource| + can :update, Spree.user_class do |resource| resource == user end - can :create, LegacyUser + can :create, Spree.user_class ############################# can :read, Order do |order, token| order.user == user || order.token && token == order.token @@ -51,6 +51,11 @@ def initialize(user) order.user == user || order.token && token == order.token end can :create, Order + + can :read, Address do |address| + address.user == user + end + ############################# can :read, Product can :index, Product diff --git a/core/app/models/spree/address.rb b/core/app/models/spree/address.rb index bc3cad421d0..ad090e80a1c 100644 --- a/core/app/models/spree/address.rb +++ b/core/app/models/spree/address.rb @@ -36,7 +36,7 @@ def full_name end def state_text - state.nil? ? state_name : (state.abbr.blank? ? state.name : state.abbr) + state.try(:abbr) || state.try(:name) || state_name end def same_as?(other) diff --git a/core/app/models/spree/adjustment.rb b/core/app/models/spree/adjustment.rb index db4083f09a2..a4d778ba60d 100644 --- a/core/app/models/spree/adjustment.rb +++ b/core/app/models/spree/adjustment.rb @@ -65,6 +65,10 @@ def update!(src = nil) set_eligibility end + def display_amount + Spree::Money.new(amount).to_s + end + private def update_adjustable diff --git a/core/app/models/spree/app_configuration.rb b/core/app/models/spree/app_configuration.rb index bcc8d34be51..3a355b11b29 100644 --- a/core/app/models/spree/app_configuration.rb +++ b/core/app/models/spree/app_configuration.rb @@ -41,6 +41,9 @@ class AppConfiguration < Preferences::Configuration preference :checkout_zone, :string, :default => nil # replace with the name of a zone if you would like to limit the countries preference :company, :boolean, :default => false # Request company field for billing and shipping addr preference :create_inventory_units, :boolean, :default => true # should only be false when track_inventory_levels is false, also disables RMA's + preference :currency, :string, :default => "USD" + preference :currency_symbol_position, :string, :default => "before" + preference :display_currency, :boolean, :default => false preference :default_country_id, :integer, :default => 214 preference :default_locale, :string, :default => Rails.application.config.i18n.default_locale || :en preference :default_meta_description, :string, :default => 'Spree demo site' @@ -48,7 +51,7 @@ class AppConfiguration < Preferences::Configuration preference :default_seo_title, :string, :default => '' preference :dismissed_spree_alerts, :string, :default => '' preference :last_check_for_spree_alerts, :string, :default => nil - preference :layout, :string, :default => '/spree/layouts/spree_application' + preference :layout, :string, :default => 'spree/layouts/spree_application' preference :logo, :string, :default => 'admin/bg/spree_50.png' preference :max_level_in_taxons_menu, :integer, :default => 1 # maximum nesting level in taxons menu preference :orders_per_page, :integer, :default => 15 diff --git a/core/app/models/spree/calculator/price_sack.rb b/core/app/models/spree/calculator/price_sack.rb index e45703abc7c..a3ea0c18bc9 100644 --- a/core/app/models/spree/calculator/price_sack.rb +++ b/core/app/models/spree/calculator/price_sack.rb @@ -1,4 +1,6 @@ require_dependency 'spree/calculator' +# For #to_d method on Ruby 1.8 +require 'bigdecimal/util' module Spree class Calculator::PriceSack < Calculator @@ -17,12 +19,12 @@ def self.description # as object we always get line items, as calculable we have Coupon, ShippingMethod def compute(object) if object.is_a?(Array) - base = object.map { |o| o.respond_to?(:amount) ? o.amount : o.to_d }.sum + base = object.map { |o| o.respond_to?(:amount) ? o.amount : BigDecimal(o.to_s) }.sum else - base = object.respond_to?(:amount) ? object.amount : object.to_d + base = object.respond_to?(:amount) ? object.amount : BigDecimal(object.to_s) end - if base >= self.preferred_minimal_amount + if base < self.preferred_minimal_amount self.preferred_normal_amount else self.preferred_discount_amount diff --git a/core/app/models/spree/credit_card.rb b/core/app/models/spree/credit_card.rb index bd850c170f5..d85ffca341a 100644 --- a/core/app/models/spree/credit_card.rb +++ b/core/app/models/spree/credit_card.rb @@ -1,6 +1,6 @@ module Spree class CreditCard < ActiveRecord::Base - has_many :payments + has_many :payments, :as => :source before_save :set_last_digits after_validation :set_card_type @@ -30,7 +30,7 @@ class << self # sets self.cc_type while we still have the card number def set_card_type - self.cc_type ||= CardDetector.type?(number) + self.cc_type ||= CardDetector.brand?(number) end def name? @@ -63,7 +63,7 @@ def brand cc_type end - scope :with_payment_profile, where('gateway_customer_profile_id IS NOT NULL') + scope :with_payment_profile, lambda { where('gateway_customer_profile_id IS NOT NULL') } def actions %w{capture void credit} diff --git a/core/app/models/spree/image.rb b/core/app/models/spree/image.rb index 089360948bf..d52138fbffc 100644 --- a/core/app/models/spree/image.rb +++ b/core/app/models/spree/image.rb @@ -9,7 +9,8 @@ class Image < Asset :styles => { :mini => '48x48>', :small => '100x100>', :product => '240x240>', :large => '600x600>' }, :default_style => :product, :url => '/spree/products/:id/:style/:basename.:extension', - :path => ':rails_root/public/spree/products/:id/:style/:basename.:extension' + :path => ':rails_root/public/spree/products/:id/:style/:basename.:extension', + :convert_options => { :all => '-strip' } # save the w,h of the original image (from which others can be calculated) # we need to look at the write-queue for images which have not been saved yet after_post_process :find_dimensions diff --git a/core/app/models/spree/inventory_unit.rb b/core/app/models/spree/inventory_unit.rb index 8eab2ad4b6c..cd020232471 100644 --- a/core/app/models/spree/inventory_unit.rb +++ b/core/app/models/spree/inventory_unit.rb @@ -5,7 +5,13 @@ class InventoryUnit < ActiveRecord::Base belongs_to :shipment belongs_to :return_authorization - scope :backorder, where(:state => 'backordered') + scope :backordered, lambda { where(:state => 'backordered') } + scope :shipped, lambda { where(:state => 'shipped') } + + def self.backorder + warn "[SPREE] Spree::InventoryUnit.backorder will be deprecated in Spree 1.3. Please use Spree::Product.backordered instead." + backordered + end attr_accessible :shipment @@ -107,8 +113,10 @@ def update_order end def restock_variant - variant.on_hand = (variant.on_hand + 1) - variant.save + if Spree::Config[:track_inventory_levels] + variant.on_hand += 1 + variant.save + end end end end diff --git a/core/app/models/spree/line_item.rb b/core/app/models/spree/line_item.rb index f481f6fdb54..9bcd364fc36 100644 --- a/core/app/models/spree/line_item.rb +++ b/core/app/models/spree/line_item.rb @@ -10,7 +10,7 @@ class LineItem < ActiveRecord::Base before_validation :copy_price validates :variant, :presence => true - validates :quantity, :numericality => { :only_integer => true, :message => I18n.t('validation.must_be_int') } + validates :quantity, :numericality => { :only_integer => true, :message => I18n.t('validation.must_be_int'), :greater_than => -1 } validates :price, :numericality => true validate :stock_availability validate :quantity_no_less_than_shipped @@ -80,6 +80,7 @@ def remove_inventory def update_order # update the order totals, etc. + order.create_tax_charge! order.update! end @@ -97,7 +98,7 @@ def stock_availability end def quantity_no_less_than_shipped - already_shipped = order.shipments.reduce(0) { |acc,s| acc + s.inventory_units.count { |i| i.variant == variant } } + already_shipped = order.shipments.reduce(0) { |acc, s| acc + s.inventory_units.shipped.where(:variant_id => variant_id).count } unless quantity >= already_shipped errors.add(:quantity, I18n.t('validation.cannot_be_less_than_shipped_units')) end diff --git a/core/app/models/spree/order.rb b/core/app/models/spree/order.rb index ebb24d4a09e..ab7847a4367 100644 --- a/core/app/models/spree/order.rb +++ b/core/app/models/spree/order.rb @@ -1,8 +1,17 @@ require 'spree/core/validators/email' +require 'spree/order/checkout' module Spree class Order < ActiveRecord::Base - include Checkout + # TODO: + # Need to use fully qualified name here because during sandbox migration + # there is a class called Checkout which conflicts if you use this: + # + # include Checkout + # + # rather than the qualified name. This will most likely be fixed with the + # 1.3 release. + include Spree::Order::Checkout checkout_flow do go_to_state :address go_to_state :delivery @@ -54,7 +63,6 @@ class Order < ActiveRecord::Base before_create :link_by_email after_create :create_tax_charge! - # TODO: validate the format of the email as well (but we can't rely on authlogic anymore to help with validation) validates :email, :presence => true, :email => true, :if => :require_email validate :has_available_shipment validate :has_available_payment @@ -98,6 +106,10 @@ def amount line_items.sum(&:amount) end + def display_total + Spree::Money.new(total) + end + def to_param number.to_s.to_url.upcase end @@ -115,6 +127,7 @@ def checkout_allowed? # Is this a free order in which case the payment step should be skipped def payment_required? + update_totals total.to_f > 0.0 end @@ -131,7 +144,7 @@ def item_count # Indicates whether there are any backordered InventoryUnits associated with the Order. def backordered? return false unless Spree::Config[:track_inventory_levels] - inventory_units.backorder.present? + inventory_units.backordered.present? end # Returns the relevant zone (if any) to be used for taxation purposes. Uses default tax zone @@ -217,7 +230,7 @@ def clone_billing_address def allow_cancel? return false unless completed? and state != 'canceled' - %w{ready backorder pending}.include? shipment_state + shipment_state.nil? || %w{ready backorder pending}.include?(shipment_state) end def allow_resume? @@ -226,6 +239,10 @@ def allow_resume? true end + def awaiting_returns? + return_authorizations.any? { |return_authorization| return_authorization.authorized? } + end + def add_variant(variant, quantity = 1) current_item = find_line_item_by_variant(variant) if current_item @@ -337,8 +354,16 @@ def credit_cards def finalize! touch :completed_at InventoryUnit.assign_opening_inventory(self) - # lock any optional adjustments (coupon promotions, etc.) - adjustments.optional.each { |adjustment| adjustment.update_column('locked', true) } + + # lock all adjustments (coupon promotions, etc.) + adjustments.each { |adjustment| adjustment.update_column('locked', true) } + + # update payment and shipment(s) states, and save + update_payment_state + shipments.each { |shipment| shipment.update!(self) } + update_shipment_state + save + deliver_order_confirmation_email self.state_changes.create({ @@ -366,8 +391,23 @@ def available_shipping_methods(display_on = nil) end def rate_hash - @rate_hash ||= available_shipping_methods(:front_end).collect do |ship_method| - next unless cost = ship_method.calculator.compute(self) + return @rate_hash if @rate_hash.present? + + # reserve one slot for each shipping method computation + computed_costs = Array.new(available_shipping_methods(:front_end).size) + + # create all the threads and kick off their execution + threads = available_shipping_methods(:front_end).each_with_index.map do |ship_method, index| + Thread.new { computed_costs[index] = [ship_method, ship_method.calculator.compute(self)] } + end + + # wait for all threads to finish + threads.map(&:join) + + # now consolidate and memoize the threaded results + @rate_hash ||= computed_costs.map do |pair| + ship_method,cost = *pair + next unless cost ShippingRate.new( :id => ship_method.id, :shipping_method => ship_method, :name => ship_method.name, @@ -395,9 +435,21 @@ def payment_method end end + def pending_payments + payments.select {|p| p.state == "checkout"} + end + def process_payments! begin - payments.each(&:process!) + pending_payments.each do |payment| + break if payment_total >= total + + payment.process! + + if payment.completed? + self.payment_total += payment.amount + end + end rescue Core::GatewayError !!Spree::Config[:allow_checkout_on_gateway_error] end @@ -415,6 +467,10 @@ def products line_items.map { |li| li.variant.product } end + def variants + line_items.map(&:variant) + end + def insufficient_stock_lines line_items.select &:insufficient_stock? end @@ -492,7 +548,7 @@ def update_shipment_state # # The +payment_state+ value helps with reporting, etc. since it provides a quick and easy way to locate Orders needing attention. def update_payment_state - + #line_item are empty when user empties cart if self.line_items.empty? || round_money(payment_total) < round_money(total) self.payment_state = 'balance_due' diff --git a/core/app/models/spree/order/checkout.rb b/core/app/models/spree/order/checkout.rb index 51d1d00ddb3..908abd3f03b 100644 --- a/core/app/models/spree/order/checkout.rb +++ b/core/app/models/spree/order/checkout.rb @@ -3,26 +3,40 @@ class Order < ActiveRecord::Base module Checkout def self.included(klass) klass.class_eval do - cattr_accessor :next_event_transitions - cattr_accessor :previous_states - cattr_accessor :checkout_flow - cattr_accessor :checkout_steps + class_attribute :next_event_transitions + class_attribute :previous_states + class_attribute :checkout_flow + class_attribute :checkout_steps def self.checkout_flow(&block) if block_given? @checkout_flow = block + define_state_machine! else @checkout_flow end end def self.define_state_machine! - self.checkout_steps = [] + # Needs to be an ordered hash to preserve flow order + self.checkout_steps = ActiveSupport::OrderedHash.new self.next_event_transitions = [] self.previous_states = [:cart] + + # Build the checkout flow using the checkout_flow defined either + # within the Order class, or a decorator for that class. + # + # This method may be called multiple times depending on if the + # checkout_flow is re-defined in a decorator or not. instance_eval(&checkout_flow) + klass = self + # To avoid a ton of warnings when the state machine is re-defined + StateMachine::Machine.ignore_method_conflicts = true + # To avoid multiple occurrences of the same transition being defined + # On first definition, state_machines will not be defined + state_machines.clear if respond_to?(:state_machines) state_machine :state, :initial => :cart do klass.next_event_transitions.each { |t| transition(t.merge(:on => :next)) } @@ -37,7 +51,7 @@ def self.define_state_machine! end event :return do - transition :to => :returned, :from => :awaiting_return + transition :to => :returned, :from => :awaiting_return, :unless => :awaiting_returns? end event :resume do @@ -50,7 +64,7 @@ def self.define_state_machine! before_transition :to => :complete do |order| begin - order.process_payments! + order.process_payments! if order.payment_required? rescue Spree::Core::GatewayError !!Spree::Config[:allow_checkout_on_gateway_error] end @@ -69,15 +83,12 @@ def self.define_state_machine! def self.go_to_state(name, options={}) self.checkout_steps[name] = options + previous_states.each do |state| + add_transition({:from => state, :to => name}.merge(options)) + end if options[:if] - previous_states.each do |state| - add_transition({:from => state, :to => name}.merge(options)) - end self.previous_states << name else - previous_states.each do |state| - add_transition({:from => state, :to => name}.merge(options)) - end self.previous_states = [name] end end diff --git a/core/app/models/spree/payment.rb b/core/app/models/spree/payment.rb index 61fb0c72c7e..5d2b818465c 100644 --- a/core/app/models/spree/payment.rb +++ b/core/app/models/spree/payment.rb @@ -18,7 +18,7 @@ class Payment < ActiveRecord::Base attr_accessible :amount, :payment_method_id, :source_attributes - scope :from_credit_card, where(:source_type => 'Spree::CreditCard') + scope :from_credit_card, lambda { where(:source_type => 'Spree::CreditCard') } scope :with_state, lambda { |s| where(:state => s) } scope :completed, with_state('completed') scope :pending, with_state('pending') diff --git a/core/app/models/spree/payment/processing.rb b/core/app/models/spree/payment/processing.rb index c4d53be97ba..64208d9f975 100644 --- a/core/app/models/spree/payment/processing.rb +++ b/core/app/models/spree/payment/processing.rb @@ -28,6 +28,7 @@ def purchase! end def capture! + return true if completed? protect_from_connection_error do check_environment @@ -47,6 +48,7 @@ def capture! end def void_transaction! + return true if void? protect_from_connection_error do check_environment @@ -104,6 +106,25 @@ def partial_credit(amount) credit!(amount) end + def gateway_options + options = { :email => order.email, + :customer => order.email, + :ip => '192.168.1.100', # TODO: Use an actual IP + :order_id => order.number } + + options.merge!({ :shipping => order.ship_total * 100, + :tax => order.tax_total * 100, + :subtotal => order.item_total * 100 }) + + options.merge!({ :currency => payment_method.preferences[:currency_code] }) if payment_method && payment_method.preferences[:currency_code] + + options.merge!({ :billing_address => order.bill_address.try(:active_merchant_hash), + :shipping_address => order.ship_address.try(:active_merchant_hash) }) + + options.merge!(:discount => promo_total) if respond_to?(:promo_total) + options + end + private def gateway_action(source, action, success_state) @@ -136,22 +157,6 @@ def record_response(response) log_entries.create({:details => response.to_yaml}, :without_protection => true) end - def gateway_options - options = { :email => order.email, - :customer => order.email, - :ip => '192.168.1.100', # TODO: Use an actual IP - :order_id => order.number } - - options.merge!({ :shipping => order.ship_total * 100, - :tax => order.tax_total * 100, - :subtotal => order.item_total * 100 }) - - options.merge!({ :currency => payment_method.preferences[:currency_code] }) if payment_method && payment_method.preferences[:currency_code] - - options.merge({ :billing_address => order.bill_address.try(:active_merchant_hash), - :shipping_address => order.ship_address.try(:active_merchant_hash) }) - end - def protect_from_connection_error begin yield diff --git a/core/app/models/spree/payment_method.rb b/core/app/models/spree/payment_method.rb index f74995214ad..3f3b32edeae 100644 --- a/core/app/models/spree/payment_method.rb +++ b/core/app/models/spree/payment_method.rb @@ -3,7 +3,7 @@ class PaymentMethod < ActiveRecord::Base DISPLAY = [:both, :front_end, :back_end] default_scope where(:deleted_at => nil) - scope :production, where(:environment => 'production') + scope :production, lambda { where(:environment => 'production') } attr_accessible :name, :description, :environment, :display_on, :active diff --git a/core/app/models/spree/preference.rb b/core/app/models/spree/preference.rb index 6e83f6cead7..ce555f088f8 100644 --- a/core/app/models/spree/preference.rb +++ b/core/app/models/spree/preference.rb @@ -4,7 +4,7 @@ class Spree::Preference < ActiveRecord::Base validates :key, :presence => true validates :value_type, :presence => true - scope :valid, where(Spree::Preference.arel_table[:key].not_eq(nil)).where(Spree::Preference.arel_table[:value_type].not_eq(nil)) + scope :valid, lambda { where(Spree::Preference.arel_table[:key].not_eq(nil)).where(Spree::Preference.arel_table[:value_type].not_eq(nil)) } # The type conversions here should match # the ones in spree::preferences::preferrable#convert_preference_value diff --git a/core/app/models/spree/preferences/preferable_class_methods.rb b/core/app/models/spree/preferences/preferable_class_methods.rb index dcc0f6a5048..f24a391db9b 100644 --- a/core/app/models/spree/preferences/preferable_class_methods.rb +++ b/core/app/models/spree/preferences/preferable_class_methods.rb @@ -15,6 +15,8 @@ def preference(name, type, *args) else if get_pending_preference(name) get_pending_preference(name) + elsif Spree::Preference.table_exists? && preference = Spree::Preference.find_by_name(name) + preference.value else send self.class.preference_default_getter_method(name) end diff --git a/core/app/models/spree/preferences/store.rb b/core/app/models/spree/preferences/store.rb index 3c2f5c44380..c4022d02508 100644 --- a/core/app/models/spree/preferences/store.rb +++ b/core/app/models/spree/preferences/store.rb @@ -23,11 +23,29 @@ def set(key, value, type) end def exist?(key) - @cache.exist? key + @cache.exist?(key) || + should_persist? && Spree::Preference.where(:key => key).exists? end def get(key) - @cache.read(key) + # return the retrieved value, if it's in the cache + if (val = @cache.read(key)).present? + return val + end + + return nil unless should_persist? + + # If it's not in the cache, maybe it's in the database, but + # has been cleared from the cache + + # does it exist in the database? + if preference = Spree::Preference.find_by_key(key) + # it does exist, so let's put it back into the cache + @cache.write(preference.key, preference.value) + + # and return the value + preference.value + end end def delete(key) diff --git a/core/app/models/spree/product.rb b/core/app/models/spree/product.rb index 4bbfe5d293a..4b399c6a1d9 100755 --- a/core/app/models/spree/product.rb +++ b/core/app/models/spree/product.rb @@ -30,8 +30,21 @@ class Product < ActiveRecord::Base belongs_to :shipping_category has_one :master, - :class_name => "Spree::Variant", - :conditions => { :is_master => true } + :class_name => 'Spree::Variant', + :conditions => { :is_master => true }, + :dependent => :destroy + + has_many :variants, + :class_name => 'Spree::Variant', + :conditions => { :is_master => false, :deleted_at => nil }, + :order => :position + + has_many :variants_including_master, + :class_name => 'Spree::Variant', + :conditions => { :deleted_at => nil }, + :dependent => :destroy + + has_many :variants_including_master_and_deleted, :class_name => 'Spree::Variant' delegate_belongs_to :master, :sku, :price, :weight, :height, :width, :depth, :is_master delegate_belongs_to :master, :cost_price if Variant.table_exists? && Variant.column_names.include?('cost_price') @@ -43,33 +56,13 @@ class Product < ActiveRecord::Base after_save :save_master after_save :set_master_on_hand_to_zero_when_product_has_variants - has_many :variants, - :class_name => "Spree::Variant", - :conditions => { :is_master => false, :deleted_at => nil }, - :order => 'position ASC' - - has_many :variants_including_master, - :class_name => "Spree::Variant", - :conditions => { :deleted_at => nil }, - :dependent => :destroy + delegate :images, :to => :master, :prefix => true + alias_method :images, :master_images - has_many :variants_with_only_master, - :class_name => "Spree::Variant", - :conditions => { :is_master => true, :deleted_at => nil }, - :dependent => :destroy + has_many :variant_images, :source => :images, :through => :variants_including_master, :order => :position accepts_nested_attributes_for :variants, :allow_destroy => true - def variant_images - Image.joins("LEFT JOIN #{Variant.quoted_table_name} ON #{Variant.quoted_table_name}.id = #{Spree::Asset.quoted_table_name}.viewable_id"). - where("(#{Spree::Asset.quoted_table_name}.viewable_type = ? AND #{Spree::Asset.quoted_table_name}.viewable_id = ?) OR - (#{Spree::Asset.quoted_table_name}.viewable_type = ? AND #{Spree::Asset.quoted_table_name}.viewable_id = ?)", Variant.name, self.master.id, Product.name, self.id). - order("#{Spree::Asset.quoted_table_name}.position"). - extend(Spree::Core::RelationSerialization) - end - - alias_method :images, :variant_images - validates :name, :price, :permalink, :presence => true attr_accessor :option_values_hash @@ -91,18 +84,29 @@ def variant_images after_initialize :ensure_master - def ensure_master - return unless new_record? - self.master ||= Variant.new + def variants_with_only_master + ActiveSupport::Deprecation.warn("[SPREE] Spree::Product#variants_with_only_master will be deprecated in Spree 1.3. Please use Spree::Product#master instead.") + master end + def to_param permalink.present? ? permalink : (permalink_was || name.to_s.to_url) end # returns true if the product has any variants (the master variant is not a member of the variants array) def has_variants? - variants.exists? + variants.any? + end + + # should product be displayed on products pages and search + def on_display? + has_stock? || Spree::Config[:show_zero_stock_products] + end + + # is this product actually available for purchase + def on_sale? + has_stock? || Spree::Config[:allow_backorders] end # returns the number of inventory units "on_hand" for this product @@ -130,6 +134,13 @@ def tax_category end end + # override the delete method to set deleted_at value + # instead of actually deleting the product. + def delete + self.update_column(:deleted_at, Time.now) + variants_including_master.update_all(:deleted_at => Time.now) + end + # Adding properties and option types on creation based on a chosen prototype attr_reader :prototype_id def prototype_id=(value) @@ -180,6 +191,10 @@ def deleted? !!deleted_at end + def available? + !(available_on.nil? || available_on.future?) + end + # split variants list into hash which shows mapping of opt value onto matching variants # eg categorise_variants_from_option(color) => {"red" -> [...], "blue" -> [...]} def categorise_variants_from_option(opt_type) @@ -215,6 +230,10 @@ def set_property(property_name, property_value) end end + def display_price + Spree::Money.new(price).to_s + end + private # Builds variants from a hash of option types & values @@ -260,6 +279,11 @@ def set_master_variant_defaults def save_master master.save if master && (master.changed? || master.new_record?) end + + def ensure_master + return unless new_record? + self.master ||= Variant.new + end end end diff --git a/core/app/models/spree/product/scopes.rb b/core/app/models/spree/product/scopes.rb index 08b5e907485..4601e4e4db1 100644 --- a/core/app/models/spree/product/scopes.rb +++ b/core/app/models/spree/product/scopes.rb @@ -27,11 +27,11 @@ def self.simple_scopes end add_search_scope :ascend_by_master_price do - joins(:variants_with_only_master).order("#{variant_table_name}.price ASC") + joins(:master).order("#{variant_table_name}.price ASC") end add_search_scope :descend_by_master_price do - joins(:variants_with_only_master).order("#{variant_table_name}.price DESC") + joins(:master).order("#{variant_table_name}.price DESC") end add_search_scope :price_between do |low, high| @@ -50,8 +50,23 @@ def self.simple_scopes # If you need products only within one taxon use # # Spree::Product.taxons_id_eq(x) + # + # If you're using count on the result of this scope, you must use the + # `:distinct` option as well: + # + # Spree::Product.in_taxon(taxon).count(:distinct => true) + # + # This is so that the count query is distinct'd: + # + # SELECT COUNT(DISTINCT "spree_products"."id") ... + # + # vs. + # + # SELECT COUNT(*) ... add_search_scope :in_taxon do |taxon| - joins(:taxons).where(Taxon.table_name => { :id => taxon.self_and_descendants.map(&:id) }) + select("DISTINCT(spree_products.id), spree_products.*"). + joins(:taxons). + where(Taxon.table_name => { :id => taxon.self_and_descendants.map(&:id) }) end # This scope selects products in all taxons AND all its descendants @@ -63,10 +78,6 @@ def self.simple_scopes taxons.first ? prepare_taxon_conditions(taxons) : scoped end - # def self.in_cached_group(product_group) - # joins(:product_groups).where('spree_product_groups_products.product_group_id' => product_group) - # end - # a scope that finds all products having property specified by name, object or id add_search_scope :with_property do |property| properties = Property.table_name @@ -113,7 +124,7 @@ def self.simple_scopes end conditions = "#{option_values}.name = ? AND #{option_values}.option_type_id = ?", value, option_type_id - select("DISTINCT spree_products.id").joins(:variants_including_master => :option_values).where(conditions) + group("spree_products.id").joins(:variants_including_master => :option_values).where(conditions) end # Finds all products which have either: @@ -188,11 +199,11 @@ def self.available(available_on = nil) add_search_scope :on_hand do variants_table = Variant.table_name - where("#{table_name}.id in (select product_id from #{variants_table} where product_id = #{table_name}.id group by product_id having sum(count_on_hand) > 0)") + where("#{table_name}.id in (select product_id from #{variants_table} where product_id = #{table_name}.id and #{variants_table}.deleted_at IS NULL group by product_id having sum(count_on_hand) > 0)") end add_search_scope :taxons_name_eq do |name| - select("DISTINCT spree_products.id").joins(:taxons).where(Taxon.arel_table[:name].eq(name)) + group("spree_products.id").joins(:taxons).where(Taxon.arel_table[:name].eq(name)) end if (ActiveRecord::Base.connection.adapter_name == 'PostgreSQL') diff --git a/core/app/models/spree/return_authorization.rb b/core/app/models/spree/return_authorization.rb index be7eca34beb..760e48b968c 100644 --- a/core/app/models/spree/return_authorization.rb +++ b/core/app/models/spree/return_authorization.rb @@ -72,6 +72,7 @@ def process_return credit.source = self credit.adjustable = order credit.save + order.return if inventory_units.all?(&:returned?) end def allow_receive? diff --git a/core/app/models/spree/shipment.rb b/core/app/models/spree/shipment.rb index 2c6b727061c..9808c2cbad4 100644 --- a/core/app/models/spree/shipment.rb +++ b/core/app/models/spree/shipment.rb @@ -26,9 +26,10 @@ class Shipment < ActiveRecord::Base make_permalink :field => :number - scope :shipped, where(:state => 'shipped') - scope :ready, where(:state => 'ready') - scope :pending, where(:state => 'pending') + scope :with_state, lambda { |s| where(:state => s) } + scope :shipped, with_state('shipped') + scope :ready, with_state('ready') + scope :pending, with_state('pending') def to_param number if number @@ -114,10 +115,11 @@ def validate_shipping_method # Determines the appropriate +state+ according to the following logic: # - # pending unless +order.payment_state+ is +paid+ + # pending unless order is complete and +order.payment_state+ is +paid+ # shipped if already shipped (ie. does not change the state) # ready all other cases def determine_state(order) + return 'pending' unless order.complete? return 'pending' if inventory_units.any? &:backordered? return 'shipped' if state == 'shipped' order.paid? ? 'ready' : 'pending' diff --git a/core/app/models/spree/shipping_method.rb b/core/app/models/spree/shipping_method.rb index 5b1537ee89c..b22e9eb3d9f 100644 --- a/core/app/models/spree/shipping_method.rb +++ b/core/app/models/spree/shipping_method.rb @@ -1,6 +1,9 @@ module Spree class ShippingMethod < ActiveRecord::Base DISPLAY = [:both, :front_end, :back_end] + + default_scope where(:deleted_at => nil) + has_many :shipments validates :name, :zone, :presence => true @@ -13,17 +16,13 @@ class ShippingMethod < ActiveRecord::Base calculated_adjustments def available?(order, display_on = nil) - displayable? && calculator.available?(order) + displayable?(display_on) && calculator.available?(order) end - def displayable? + def displayable?(display_on) (self.display_on == display_on.to_s || self.display_on.blank?) end - def calculator_available?(order) - caluclator.available?(order) - end - def within_zone?(order) zone && zone.include?(order.ship_address) end diff --git a/core/app/models/spree/shipping_rate.rb b/core/app/models/spree/shipping_rate.rb index aba92108a41..35668556af6 100644 --- a/core/app/models/spree/shipping_rate.rb +++ b/core/app/models/spree/shipping_rate.rb @@ -5,5 +5,15 @@ def initialize(attributes = {}) self.send("#{k}=", v) end end + + def display_price + if Spree::Config[:shipment_inc_vat] + price = (1 + Spree::TaxRate.default) * cost + else + price = cost + end + + Spree::Money.new(price) + end end end diff --git a/core/app/models/spree/tax_rate.rb b/core/app/models/spree/tax_rate.rb index 5affe2666c3..bebe82eda32 100644 --- a/core/app/models/spree/tax_rate.rb +++ b/core/app/models/spree/tax_rate.rb @@ -20,7 +20,7 @@ class TaxRate < ActiveRecord::Base calculated_adjustments scope :by_zone, lambda { |zone| where(:zone_id => zone) } - attr_accessible :amount, :tax_category_id, :calculator, :zone_id, :included_in_price + attr_accessible :amount, :tax_category_id, :calculator, :zone_id, :included_in_price, :name, :show_rate_in_label # Gets the array of TaxRates appropriate for the specified order def self.match(order) @@ -73,7 +73,9 @@ def adjust(order) private def create_label - "#{tax_category.name} #{amount * 100}%" + label = "" + label << (name.present? ? name : tax_category.name) + " " + label << (show_rate_in_label? ? "#{amount * 100}%" : "") end end end diff --git a/core/app/models/spree/taxon.rb b/core/app/models/spree/taxon.rb index 6c939671dde..ecd861da1aa 100644 --- a/core/app/models/spree/taxon.rb +++ b/core/app/models/spree/taxon.rb @@ -51,5 +51,12 @@ def active_products scope end + def pretty_name + ancestor_chain = self.ancestors.inject("") do |name, ancestor| + name += "#{ancestor.name} -> " + end + ancestor_chain + "#{name}" + end + end end diff --git a/core/app/models/spree/variant.rb b/core/app/models/spree/variant.rb index aee12b30a3d..9a87cae7c7a 100644 --- a/core/app/models/spree/variant.rb +++ b/core/app/models/spree/variant.rb @@ -6,7 +6,7 @@ class Variant < ActiveRecord::Base :tax_category_id, :shipping_category_id, :meta_description, :meta_keywords, :tax_category - attr_accessible :name, :presentation, :cost_price, + attr_accessible :name, :presentation, :cost_price, :lock_version, :position, :on_hand, :option_value_ids, :product_id, :option_values_attributes, :price, :weight, :height, :width, :depth, :sku @@ -21,48 +21,33 @@ class Variant < ActiveRecord::Base validates :cost_price, :numericality => { :greater_than_or_equal_to => 0, :allow_nil => true } if self.table_exists? && self.column_names.include?('cost_price') validates :count_on_hand, :numericality => true + after_save :process_backorders after_save :recalculate_product_on_hand, :if => :is_master? # default variant scope only lists non-deleted variants - scope :active, where(:deleted_at => nil) - scope :deleted, where('deleted_at IS NOT NULL') + scope :active, lambda { where(:deleted_at => nil) } + scope :deleted, lambda { where('deleted_at IS NOT NULL') } # Returns number of inventory units for this variant (new records haven't been saved to database, yet) def on_hand Spree::Config[:track_inventory_levels] ? count_on_hand : (1.0 / 0) # Infinity end - # Adjusts the inventory units to match the given new level. + # set actual attribute def on_hand=(new_level) if Spree::Config[:track_inventory_levels] - new_level = new_level.to_i - - # increase Inventory when - if new_level > on_hand - # fill backordered orders before creating new units - backordered_units = inventory_units.with_state('backordered') - backordered_units.slice(0, new_level).each(&:fill_backorder) - new_level -= backordered_units.length - end - self.count_on_hand = new_level else raise 'Cannot set on_hand value when Spree::Config[:track_inventory_levels] is false' end end - # strips all non-price-like characters from the price. def price=(price) - if price.present? - self[:price] = price.to_s.gsub(/[^0-9\.-]/, '').to_f - end + self[:price] = parse_price(price) if price.present? end - # and cost_price def cost_price=(price) - if price.present? - self[:cost_price] = price.to_s.gsub(/[^0-9\.-]/, '').to_f - end + self[:cost_price] = parse_price(price) if price.present? end # returns number of units currently on backorder for this variant. @@ -140,6 +125,38 @@ def option_value(opt_name) private + def process_backorders + if count_changes = changes['count_on_hand'] + new_level = count_changes.last + + if Spree::Config[:track_inventory_levels] + new_level = new_level.to_i + + # update backorders if level is positive + if new_level > 0 + # fill backordered orders before creating new units + backordered_units = inventory_units.with_state('backordered') + backordered_units.slice(0, new_level).each(&:fill_backorder) + new_level -= backordered_units.length + end + + self.update_attribute_without_callbacks(:count_on_hand, new_level) + end + end + end + + # strips all non-price-like characters from the price, taking into account locale settings + def parse_price(price) + return price unless price.is_a?(String) + + separator, delimiter = I18n.t([:'number.currency.format.separator', :'number.currency.format.delimiter']) + non_price_characters = /[^0-9\-#{separator}]/ + price.gsub!(non_price_characters, '') # strip everything else first + price.gsub!(separator, '.') unless separator == '.' # then replace the locale-specific decimal separator with the standard separator if necessary + + price.to_d + end + # Ensures a new variant takes the product master price when price is not supplied def check_price if price.nil? diff --git a/core/app/views/spree/admin/adjustments/_adjustments_table.html.erb b/core/app/views/spree/admin/adjustments/_adjustments_table.html.erb index 250f8a55ded..efd4257cbcd 100644 --- a/core/app/views/spree/admin/adjustments/_adjustments_table.html.erb +++ b/core/app/views/spree/admin/adjustments/_adjustments_table.html.erb @@ -12,7 +12,7 @@ <%= adjustment.created_at.to_s(:date_time24) %> <%= adjustment.label %> - <%= number_to_currency adjustment.amount %> + <%= adjustment.display_amount %> <%= link_to_edit adjustment, :class => 'edit' %>   diff --git a/core/app/views/spree/admin/banners/_gateway.html.erb b/core/app/views/spree/admin/banners/_gateway.html.erb new file mode 100644 index 00000000000..d6e7be8508c --- /dev/null +++ b/core/app/views/spree/admin/banners/_gateway.html.erb @@ -0,0 +1,14 @@ +<% if !spree_current_user.dismissed_banner?(:gateway) && + Spree::PaymentMethod.production.where("type != 'Spree::Gateway::Bogus'").empty? %> + + + +<% end %> diff --git a/core/app/views/spree/admin/general_settings/edit.html.erb b/core/app/views/spree/admin/general_settings/edit.html.erb index fdbe3423612..eea52c8f202 100644 --- a/core/app/views/spree/admin/general_settings/edit.html.erb +++ b/core/app/views/spree/admin/general_settings/edit.html.erb @@ -4,15 +4,33 @@ <%= form_tag admin_general_settings_path, :method => :put do %>
- <% @preferences.each do |key| - type = Spree::Config.preference_type(key) %> - <%= label_tag(key, t(key) + ': ') + tag(:br) if type != :boolean %> - <%= preference_field_tag(key, Spree::Config[key], :type => type) %> - <%= label_tag(key, t(key)) + tag(:br) if type == :boolean %> - <% end %> + <% @preferences.each do |key| + type = Spree::Config.preference_type(key) %> + <%= label_tag(key, t(key) + ': ') + tag(:br) if type != :boolean %> + <%= preference_field_tag(key, Spree::Config[key], :type => type) %> + <%= label_tag(key, t(key)) + tag(:br) if type == :boolean %> + <% end %> + +

+ <%= label_tag :currency, t(:currency) %> + <%= select_tag :currency, currency_options %> +

+ +

+ <%= t(:currency_symbol_position) %>
+ <%= radio_button_tag :currency_symbol_position, "before" %> + <%= label_tag :currency_symbol_position_before, Spree::Money.new(10, :symbol_position => "before") %>
+ <%= radio_button_tag :currency_symbol_position, "after" %> + <%= label_tag :currency_symbol_position_after, Spree::Money.new(10, :symbol_position => "after") %> +

+

<%= button t(:update) %> <%= t(:or) %> <%= link_to t(:cancel), admin_general_settings_url %>

<% end %> + + diff --git a/core/app/views/spree/admin/general_settings/show.html.erb b/core/app/views/spree/admin/general_settings/show.html.erb index 3ccf279b619..b14d395e2ba 100644 --- a/core/app/views/spree/admin/general_settings/show.html.erb +++ b/core/app/views/spree/admin/general_settings/show.html.erb @@ -9,6 +9,12 @@ <%= Spree::Config[key] %> <% end %> + + + <%= t(:dollar_amounts_displayed_as, :example => Spree::Money.new(20.99)) %> + + + <%= Spree::Config[:allow_ssl_in_production] ? t(:ssl_will_be_used_in_production_mode) : t(:ssl_will_not_be_used_in_production_mode) %> diff --git a/core/app/views/spree/admin/images/_form.html.erb b/core/app/views/spree/admin/images/_form.html.erb index 94c5db6071e..a7d9d84dc5d 100644 --- a/core/app/views/spree/admin/images/_form.html.erb +++ b/core/app/views/spree/admin/images/_form.html.erb @@ -3,14 +3,10 @@ <%= t(:filename) %>: <%= f.file_field :attachment %> - <% if @product.has_variants? %> - - <%= Spree::Variant.model_name.human %>: - <%= f.select :viewable_id, @variants %> - - <% else %> - <%= hidden_field_tag :product_id, @product.id %> - <% end %> + + <%= Spree::Variant.model_name.human %>: + <%= f.select :viewable_id, @variants %> + <%= t(:alt_text) %>: <%= f.text_area :alt %> diff --git a/core/app/views/spree/admin/orders/_form.html.erb b/core/app/views/spree/admin/orders/_form.html.erb index bad7df4cf61..9236133c465 100644 --- a/core/app/views/spree/admin/orders/_form.html.erb +++ b/core/app/views/spree/admin/orders/_form.html.erb @@ -21,15 +21,15 @@ <%= t(:subtotal) %>: - <%= number_to_currency @order.item_total %> + <%= money(@order.item_total) %> - <% @order.adjustments.each do |adjustment| %> + <% @order.adjustments.eligible.each do |adjustment| %> <%= adjustment.label %> - <%= number_to_currency adjustment.amount %> + <%= adjustment.display_amount %> <% end %> @@ -37,7 +37,7 @@ <%= t(:order_total) %>: - <%= number_to_currency @order.total %> + <%= @order.display_total %> diff --git a/core/app/views/spree/admin/orders/_line_item.html.erb b/core/app/views/spree/admin/orders/_line_item.html.erb index bc1d379ec15..c179fc88d9a 100644 --- a/core/app/views/spree/admin/orders/_line_item.html.erb +++ b/core/app/views/spree/admin/orders/_line_item.html.erb @@ -2,9 +2,9 @@ <%=f.object.variant.product.name%> <%= "(#{f.object.variant.options_text}))" unless f.object.variant.option_values.empty? %> - <%= number_to_currency f.object.price %> + <%= money(f.object.price) %> <%= f.number_field :quantity, :min => 0, :style => "width:30px;", :class => "qty" %> - <%= number_to_currency (f.object.price * f.object.quantity) %> + <%= money(f.object.price * f.object.quantity) %> <%= link_to_delete f.object, {:url => admin_order_line_item_url(@order.number, f.object) } %> diff --git a/core/app/views/spree/admin/orders/customer_details/_form.html.erb b/core/app/views/spree/admin/orders/customer_details/_form.html.erb index 2e5d17e9e9b..6eed27752a9 100644 --- a/core/app/views/spree/admin/orders/customer_details/_form.html.erb +++ b/core/app/views/spree/admin/orders/customer_details/_form.html.erb @@ -43,6 +43,6 @@

<% content_for :head do %> - <%= javascript_include_tag states_url, 'admin/address_states.js' %> + <%= javascript_include_tag states_path, 'admin/address_states.js' %> <% end %> diff --git a/core/app/views/spree/admin/orders/index.html.erb b/core/app/views/spree/admin/orders/index.html.erb index 6878a316a61..dd80fab4969 100644 --- a/core/app/views/spree/admin/orders/index.html.erb +++ b/core/app/views/spree/admin/orders/index.html.erb @@ -32,7 +32,7 @@ <%= link_to t("payment_states.#{order.payment_state}"), admin_order_payments_path(order) if order.payment_state %> <%= link_to t("shipment_states.#{order.shipment_state}"), admin_order_shipments_path(order) if order.shipment_state %> <%= order.email %> - <%= number_to_currency order.total %> + <%= order.display_total %> <%= link_to_edit_url edit_admin_order_path(order), :title => "admin_edit_#{dom_id(order)}" %> @@ -82,6 +82,10 @@ <%= f.check_box :completed_at_not_null, {:checked => @show_only_completed}, '1', '' %> <%= label_tag nil, t(:show_only_complete_orders) %>

+

+ <%= f.check_box :inventory_units_shipment_id_null, { }, '1', '0' %> + <%= label_tag nil, t(:show_only_unfulfilled_orders) %> +

<%= button t(:search) %>

diff --git a/core/app/views/spree/admin/payments/_list.html.erb b/core/app/views/spree/admin/payments/_list.html.erb index 8c1ffb52439..3959febed04 100644 --- a/core/app/views/spree/admin/payments/_list.html.erb +++ b/core/app/views/spree/admin/payments/_list.html.erb @@ -9,7 +9,7 @@ <% payments.each do |payment| %> <%= payment.created_at.to_s(:date_time24) %> - <%= number_to_currency payment.amount %> + <%= money(payment.amount) %> <%= link_to payment_method_name(payment), admin_order_payment_path(@order, payment) %> <%= t(payment.state, :scope => :payment_states, :default => payment.state.capitalize) %> diff --git a/core/app/views/spree/admin/payments/_transaction_list.html.erb b/core/app/views/spree/admin/payments/_transaction_list.html.erb deleted file mode 100644 index 0fe24fd76aa..00000000000 --- a/core/app/views/spree/admin/payments/_transaction_list.html.erb +++ /dev/null @@ -1,24 +0,0 @@ -
- - <%= t(:transactions) %> - - - - - - - - - <% txns.each do |txn| %> - - - - - - - <% end %> -
<%= t(:type) %><%= t('spree.date') %><%= t(:amount) %><%= t(:response_code) %>
- <%= txn.txn_type_name %> - <%= txn.created_at.to_date %><%= number_to_currency txn.amount %><%= txn.response_code %>
- -
diff --git a/core/app/views/spree/admin/payments/index.html.erb b/core/app/views/spree/admin/payments/index.html.erb index 62573b61be6..09f760db8a2 100644 --- a/core/app/views/spree/admin/payments/index.html.erb +++ b/core/app/views/spree/admin/payments/index.html.erb @@ -12,7 +12,7 @@ <%= render :partial => 'spree/admin/shared/order_tabs', :locals => { :current => 'Payments' } %> <% if @order.outstanding_balance? %> -

<%= @order.outstanding_balance < 0 ? t(:credit_owed) : t(:balance_due) %> <%= number_to_currency @order.outstanding_balance %>

+

<%= @order.outstanding_balance < 0 ? t(:credit_owed) : t(:balance_due) %> <%= money(@order.outstanding_balance) %>

<% end %>

<%= t(:payments) %>

diff --git a/core/app/views/spree/admin/product_properties/index.html.erb b/core/app/views/spree/admin/product_properties/index.html.erb index bdf66ecdc81..776c6ae7ead 100644 --- a/core/app/views/spree/admin/product_properties/index.html.erb +++ b/core/app/views/spree/admin/product_properties/index.html.erb @@ -39,7 +39,7 @@ <% end %> <%= javascript_tag do -%> - var properties = [<%= @properties.map{|id| "'#{id}'"}.join(', ') %>]; + var properties = <%= raw(@properties.to_json) %>; $("#product_properties input.autocomplete").live("keydown", function(){ already_auto_completed = $(this).is('ac_input'); diff --git a/core/app/views/spree/admin/products/_form.html.erb b/core/app/views/spree/admin/products/_form.html.erb index bcdd619cd01..efab02934a9 100644 --- a/core/app/views/spree/admin/products/_form.html.erb +++ b/core/app/views/spree/admin/products/_form.html.erb @@ -88,7 +88,7 @@ <%= f.field_container :taxons do %> <%= f.label :taxon_ids, t(:taxons) %>
- <%= f.select :taxon_ids, taxon_options_for(@product), {}, :class => "select2", :multiple => true %> + <%= f.hidden_field :taxon_ids, :value => @product.taxon_ids.join(',') %> <% end %> <%= f.field_container :option_types do %> @@ -112,11 +112,10 @@
- + $('.select2-container').css({width: '20em'}) + +<% end %> diff --git a/core/app/views/spree/admin/products/index.html.erb b/core/app/views/spree/admin/products/index.html.erb index 4e3077f6cb2..9f3261d7f88 100644 --- a/core/app/views/spree/admin/products/index.html.erb +++ b/core/app/views/spree/admin/products/index.html.erb @@ -24,7 +24,7 @@ id="<%= spree_dom_id product %>" data-hook="admin_products_index_rows"> <%= product.sku rescue '' %> <%= link_to product.try(:name), edit_admin_product_path(product) %> - <%= number_to_currency product.price rescue '' %> + <%= money(product.price) rescue '' %> <%= link_to_edit product, :class => 'edit' unless product.deleted? %>   diff --git a/core/app/views/spree/admin/products/new.html.erb b/core/app/views/spree/admin/products/new.html.erb index c4c9dd50fac..ba6541a5a0b 100644 --- a/core/app/views/spree/admin/products/new.html.erb +++ b/core/app/views/spree/admin/products/new.html.erb @@ -59,7 +59,7 @@ //"; - var prototype_select = $('#product_prototype_id'); + var prototype_select = $('#product_prototype_id'); prototype_select.change(function() { var id = prototype_select.val(); if (id.length) { diff --git a/core/app/views/spree/admin/reports/sales_total.html.erb b/core/app/views/spree/admin/reports/sales_total.html.erb index 05c8d00d9d5..c3bc3998759 100644 --- a/core/app/views/spree/admin/reports/sales_total.html.erb +++ b/core/app/views/spree/admin/reports/sales_total.html.erb @@ -4,15 +4,15 @@ <%= t(:item_total) %>: - <%= number_to_currency @item_total %> + <%= money @item_total %> <%= t(:adjustment_total) %>: - <%= number_to_currency @adjustment_total %> + <%= money @adjustment_total %> <%= t(:sales_total) %>: - <%= number_to_currency @sales_total %> + <%= money @sales_total %> diff --git a/core/app/views/spree/admin/return_authorizations/_form.html.erb b/core/app/views/spree/admin/return_authorizations/_form.html.erb index b3c396fbfff..ac0754ed3b5 100644 --- a/core/app/views/spree/admin/return_authorizations/_form.html.erb +++ b/core/app/views/spree/admin/return_authorizations/_form.html.erb @@ -20,8 +20,8 @@ <% elsif units.select(&:shipped?).empty? %> 0 <% else %> - <%= text_field_tag "return_quantity[#{variant.id}]", - @return_authorization.inventory_units.group_by(&:variant)[variant].try(:size) || 0, {:style => 'width:30px;'} %> + <%= number_field_tag "return_quantity[#{variant.id}]", + @return_authorization.inventory_units.group_by(&:variant)[variant].try(:size) || 0, {:style => 'width:50px;', :min => 0} %> <% end %> @@ -31,9 +31,9 @@ <%= f.field_container :amount do %> <%= f.label :amount, t(:amount) %> *
<% if @return_authorization.received? %> - <%= number_to_currency @return_authorization.amount %> + <%= money @return_authorization.amount %> <% else %> - <%= f.text_field :amount, {:style => 'width:80px;'} %> <%= t(:rma_value) %>: + <%= f.text_field :amount, {:style => 'width:80px;'} %> <%= t(:rma_value) %>: 0.00 <%= f.error_message_on :amount %> <% end %> <% end %> @@ -45,35 +45,19 @@ <% end %> - <% content_for :head do %> - <%= javascript_tag do -%> - var variant_prices = new Array(); - <% @return_authorization.order.inventory_units.group_by(&:variant).each do | variant, units| %> - variant_prices[<%= variant.id.to_s %>] = <%= variant.price %>; - <% end %> - - function calculate_rma_price(object, value){ - var rma_amount = 0; - - $.each($("td.return_quantity input"), function(i, inpt){ - var variant_id = $(inpt).attr('id').replace("return_quantity_", ""); - rma_amount += variant_prices[variant_id] * $(inpt).val() - }); + diff --git a/core/app/views/spree/admin/return_authorizations/index.html.erb b/core/app/views/spree/admin/return_authorizations/index.html.erb index 141d0c62d24..bab94bab5f3 100644 --- a/core/app/views/spree/admin/return_authorizations/index.html.erb +++ b/core/app/views/spree/admin/return_authorizations/index.html.erb @@ -23,7 +23,7 @@ <%= return_authorization.number %> <%= t(return_authorization.state.downcase) %> - <%= number_to_currency return_authorization.amount %> + <%= money return_authorization.amount %> <%= return_authorization.created_at.to_s(:date_time24) %> <%= link_to_edit return_authorization %> diff --git a/core/app/views/spree/admin/search/users.rabl b/core/app/views/spree/admin/search/users.rabl new file mode 100644 index 00000000000..6e6d3932f05 --- /dev/null +++ b/core/app/views/spree/admin/search/users.rabl @@ -0,0 +1,32 @@ +object false +child @users => :users do + attributes :email, :id + address_fields = [:firstname, :lastname, + :address1, :address2, + :city, :zipcode, + :phone, :state_name, + :state_id, :country_id, + :company] + + child :ship_address => :ship_address do + attributes *address_fields + child :state do + attributes :name + end + + child :country do + attributes :name + end + end + + child :bill_address => :bill_address do + attributes *address_fields + child :state do + attributes :name + end + + child :country do + attributes :name + end + end +end diff --git a/core/app/views/spree/admin/shared/_head.html.erb b/core/app/views/spree/admin/shared/_head.html.erb index 3f285f287b7..4c73d3078df 100644 --- a/core/app/views/spree/admin/shared/_head.html.erb +++ b/core/app/views/spree/admin/shared/_head.html.erb @@ -6,22 +6,12 @@ <%= stylesheet_link_tag 'admin/all' %> <%= javascript_include_tag 'admin/all' %> -<%= javascript_tag do %> - Spree.routes = <%== { - :product_search => spree.admin_products_path(:format => 'json'), - :product_search_basic => spree.admin_products_path(:format => 'json', :json_format => 'basic', :limit => 10), - :user_search => spree.admin_search_users_path(:format => 'json', :limit => 10) - }.to_json %>; - - strings = <%== - [:no_results, :type_to_search, :searching]. - inject({}){|memo, item| {item => t(item) }}.to_json - %> -<% end %> +<%= render "spree/admin/shared/translations" %> +<%= render "spree/admin/shared/routes" %> <%= javascript_tag do -%> jQuery.alerts.dialogClass = 'spree'; - <%== "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %> + <%== "var AUTH_TOKEN = #{form_authenticity_token.inspect};" %> <% end -%> <%= yield :head %> diff --git a/core/app/views/spree/admin/shared/_order_details.html.erb b/core/app/views/spree/admin/shared/_order_details.html.erb index 49c59f6475b..e68fdd55552 100644 --- a/core/app/views/spree/admin/shared/_order_details.html.erb +++ b/core/app/views/spree/admin/shared/_order_details.html.erb @@ -10,16 +10,16 @@ <% @order.line_items.each do |item| %> <%= item.variant.product.name %> <%= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? %> - <%= number_to_currency item.price %> + <%= money item.price %> <%= item.quantity %> - <%= number_to_currency (item.price * item.quantity) %> + <%= money(item.price * item.quantity) %> <% end %> <%= t(:subtotal) %>: - <%= number_to_currency @order.item_total %> + <%= money @order.item_total %> @@ -27,14 +27,14 @@ <% next if (adjustment.originator_type == 'Spree::TaxRate') and (adjustment.amount == 0) %> <%= adjustment.label %>: - <%= number_to_currency adjustment.amount %> + <%= money adjustment.amount %> <% end %> <%= t(:order_total) %>: - <%= number_to_currency @order.total %> + <%= money @order.total %> <% if order.price_adjustment_totals.present? %> @@ -42,7 +42,7 @@ <% @order.price_adjustment_totals.keys.each do |key| %> <%= key %> - <%= number_to_currency @order.price_adjustment_totals[key] %> + <%= money @order.price_adjustment_totals[key] %> <% end %> diff --git a/core/app/views/spree/admin/shared/_order_tabs.html.erb b/core/app/views/spree/admin/shared/_order_tabs.html.erb index 9ed65743a36..9e9df67222f 100644 --- a/core/app/views/spree/admin/shared/_order_tabs.html.erb +++ b/core/app/views/spree/admin/shared/_order_tabs.html.erb @@ -4,10 +4,11 @@

<%= t(:order) %> #<%= @order.number%>

<%= t(:status) %>: <%= t(@order.state, :scope => :order_state) %>
-
<%= t(:total) %>: <%= number_to_currency @order.total %>
+
<%= t(:total) %>: <%= money @order.total %>
<% if @order.completed? %>
<%= t(:shipment) %>: <%= t(@order.shipment_state, :scope => :shipment_state, :default => [:missing, "none"]) %>
<%= t(:payment) %>: <%= t(@order.payment_state, :scope => :payment_states, :default => [:missing, "none"]) %>
+
<%= t(:date_completed) %>: <%= I18n.l(@order.completed? ? @order.completed_at : @order.created_at) %>
<% end %>
diff --git a/core/app/views/spree/admin/shared/_routes.html.erb b/core/app/views/spree/admin/shared/_routes.html.erb new file mode 100644 index 00000000000..a15905645f8 --- /dev/null +++ b/core/app/views/spree/admin/shared/_routes.html.erb @@ -0,0 +1,8 @@ + diff --git a/core/app/views/spree/admin/shared/_translations.html.erb b/core/app/views/spree/admin/shared/_translations.html.erb new file mode 100644 index 00000000000..5b4c7bfd50f --- /dev/null +++ b/core/app/views/spree/admin/shared/_translations.html.erb @@ -0,0 +1,26 @@ + diff --git a/core/app/views/spree/admin/shared/_update_order_state.js b/core/app/views/spree/admin/shared/_update_order_state.js index 2469f92bff1..6eb62993783 100644 --- a/core/app/views/spree/admin/shared/_update_order_state.js +++ b/core/app/views/spree/admin/shared/_update_order_state.js @@ -1,5 +1,5 @@ $('#order_tab_summary h5#order_status').html('<%= j t(:status) %>: <%= j t(@order.state, :scope => :order_state) %>'); -$('#order_tab_summary h5#order_total').html('<%= j t(:total) %>: <%= j number_to_currency(@order.total) %>'); +$('#order_tab_summary h5#order_total').html('<%= j t(:total) %>: <%= j @order.display_total.to_s %>'); <% if @order.completed? %> $('#order_tab_summary h5#payment_status').html('<%= j t(:payment) %>: <%= j t(@order.payment_state, :scope => :payment_states, :default => [:missing, "none"]) %>'); diff --git a/core/app/views/spree/admin/shipments/edit.html.erb b/core/app/views/spree/admin/shipments/edit.html.erb index f203bf7f552..748e3a6f0a7 100644 --- a/core/app/views/spree/admin/shipments/edit.html.erb +++ b/core/app/views/spree/admin/shipments/edit.html.erb @@ -14,7 +14,7 @@

<%= t(:shipment) %> #<%= @shipment.number%> (<%= t(@shipment.state.to_sym, :scope => :state_names, :default => @shipment.state.to_s.humanize) %>)

<% if @shipment.cost %> -

<%= t(:charges) %> <%= number_to_currency @shipment.cost %>

+

<%= t(:charges) %> <%= money @shipment.cost %>

<% end %> <%= render :partial => 'spree/shared/error_messages', :locals => { :target => @shipment } %> diff --git a/core/app/views/spree/admin/shipments/index.html.erb b/core/app/views/spree/admin/shipments/index.html.erb index e8a2e23d9fc..4d79bdbb938 100644 --- a/core/app/views/spree/admin/shipments/index.html.erb +++ b/core/app/views/spree/admin/shipments/index.html.erb @@ -23,7 +23,7 @@ <%= shipment.number %> <%= shipment.shipping_method.name if shipment.shipping_method %> - <%= number_to_currency shipment.cost %> + <%= money shipment.cost %> <%= shipment.tracking %> <%= t(shipment.state.to_sym, :scope => :state_names, :default => shipment.state.to_s.humanize) %> <%= shipment.shipped_at.to_s(:date_time24) if shipment.shipped_at %> diff --git a/core/app/views/spree/admin/tax_rates/_form.html.erb b/core/app/views/spree/admin/tax_rates/_form.html.erb index 78407e11c1b..f6edd5e598e 100644 --- a/core/app/views/spree/admin/tax_rates/_form.html.erb +++ b/core/app/views/spree/admin/tax_rates/_form.html.erb @@ -4,6 +4,10 @@ <%= f.label :zone, t(:zone) %> <%= f.collection_select(:zone_id, @available_zones, :id, :name) %> + + <%= f.label :name, t(:name) %> + <%= f.text_field :name %> + <%= f.label :tax_category_id, t(:tax_category) %> <%= f.collection_select(:tax_category_id, @available_categories,:id, :name) %> @@ -16,6 +20,10 @@ <%= f.label :amount, t(:rate) %> <%= f.text_field :amount %> + + <%= f.label :show_rate_in_label, t(:show_rate_in_label) %> + <%= f.check_box :show_rate_in_label %> + <%= render :partial => 'spree/admin/shared/calculator_fields', :locals => { :f => f } %> diff --git a/core/app/views/spree/admin/tax_rates/index.html.erb b/core/app/views/spree/admin/tax_rates/index.html.erb index 54f437c73f5..4664b9bf73b 100644 --- a/core/app/views/spree/admin/tax_rates/index.html.erb +++ b/core/app/views/spree/admin/tax_rates/index.html.erb @@ -11,9 +11,11 @@ <%= t(:zone) %> + <%= t(:name) %> <%= t(:category) %> <%= t(:amount) %> <%= t(:included_in_price) %> + <%= t(:show_rate_in_label) %> <%= t(:calculator) %> <%= t(:action) %> @@ -21,10 +23,12 @@ <% @tax_rates.each do |tax_rate|%> - <%=tax_rate.zone.name %> + <%=tax_rate.zone.try(:name) || t(:not_available) %> + <%=tax_rate.name %> <%=tax_rate.tax_category.try(:name) || t(:not_available) %> <%=tax_rate.amount %> <%=tax_rate.included_in_price %> + <%=tax_rate.show_rate_in_label %> <%=tax_rate.calculator.to_s %> <%= link_to_edit tax_rate %>   diff --git a/core/app/views/spree/admin/taxonomies/edit.erb b/core/app/views/spree/admin/taxonomies/edit.erb index c7114bfac9a..064f638eeb8 100755 --- a/core/app/views/spree/admin/taxonomies/edit.erb +++ b/core/app/views/spree/admin/taxonomies/edit.erb @@ -28,14 +28,14 @@ var initial = [ { "attr" : { "id" : "<%= @taxonomy.root.id %>", "rel" : "root" }, - "data" : "<%= @taxonomy.root.name %>", + "data" : "<%= escape_javascript(raw(@taxonomy.root.name)) %>", "state" : "open", "children" : [ <% @taxonomy.root.children.each_with_index do |taxon,i| %> { "attr" : { "id" : "<%= taxon.id %>"}, - "data" : "<%= taxon.name %>" + "data" : "<%= escape_javascript(raw(taxon.name)) %>" <% unless taxon.children.empty? %> ,"state" : "closed" <% end %> diff --git a/core/app/views/spree/admin/taxonomies/get_children.json.erb b/core/app/views/spree/admin/taxonomies/get_children.json.erb index 66177f4560f..003aec9d85f 100755 --- a/core/app/views/spree/admin/taxonomies/get_children.json.erb +++ b/core/app/views/spree/admin/taxonomies/get_children.json.erb @@ -1,7 +1,7 @@ [<% @taxons.each_with_index do |t,i| %> { "attr" : { "id" : "<%= t.id %>" }, - "data" : "<%= t.name %>" + "data" : "<%= escape_javascript(raw(t.name)) %>" <% unless t.children.empty? %> ,"state" : "closed" <% end %> diff --git a/core/app/views/spree/admin/taxons/search.rabl b/core/app/views/spree/admin/taxons/search.rabl new file mode 100644 index 00000000000..ac5f36f9794 --- /dev/null +++ b/core/app/views/spree/admin/taxons/search.rabl @@ -0,0 +1,5 @@ +object false +child(@taxons => :taxons) do + attributes :name, :pretty_name, :id +end + diff --git a/core/app/views/spree/admin/variants/_form.html.erb b/core/app/views/spree/admin/variants/_form.html.erb index 6a8bdb0aa3d..f848989e490 100644 --- a/core/app/views/spree/admin/variants/_form.html.erb +++ b/core/app/views/spree/admin/variants/_form.html.erb @@ -21,10 +21,10 @@ <%= f.text_field :sku %>

<%= f.label :price, t(:price) %>:
- <%= f.text_field :price, :value => number_with_precision(@variant.price, :precision => 2) %>

+ <%= f.text_field :price, :value => number_to_currency(@variant.price, :unit => '') %>

<%= f.label :cost_price, t(:cost_price) %>:
- <%= f.text_field :cost_price, :value => number_with_precision(@variant.cost_price, :precision => 2) %>

+ <%= f.text_field :cost_price, :value => number_to_currency(@variant.cost_price, :unit => '') %>

<% if Spree::Config[:track_inventory_levels] %>

<%= f.label :on_hand, t(:on_hand) %>:
diff --git a/core/app/views/spree/admin/variants/index.html.erb b/core/app/views/spree/admin/variants/index.html.erb index 9083e705345..f81fce2a8f7 100644 --- a/core/app/views/spree/admin/variants/index.html.erb +++ b/core/app/views/spree/admin/variants/index.html.erb @@ -16,7 +16,7 @@ <% next if variant.option_values.empty? %> data-hook="variants_row"> <%= variant.options_text %> - <%= number_to_currency variant.price %> + <%= money variant.price %> <%= variant.sku %> <%= variant.on_hand %> diff --git a/core/app/views/spree/checkout/_address.html.erb b/core/app/views/spree/checkout/_address.html.erb index 12192089805..dfd5370e2ff 100644 --- a/core/app/views/spree/checkout/_address.html.erb +++ b/core/app/views/spree/checkout/_address.html.erb @@ -1,10 +1,4 @@

-<% unless @order.email? %> -

- <%= form.label :email %>
- <%= form.text_field :email %> -

-<% end %>
<%= form.fields_for :bill_address do |bill_form| %> <%= t(:billing_address) %> diff --git a/core/app/views/spree/checkout/_confirm.html.erb b/core/app/views/spree/checkout/_confirm.html.erb index 80a9c22918f..51fb3639103 100644 --- a/core/app/views/spree/checkout/_confirm.html.erb +++ b/core/app/views/spree/checkout/_confirm.html.erb @@ -8,4 +8,5 @@
<%= submit_tag t(:place_order), :class => 'continue button primary' %> +
diff --git a/core/app/views/spree/checkout/_delivery.html.erb b/core/app/views/spree/checkout/_delivery.html.erb index 05c5608199b..a9d0f83f1aa 100644 --- a/core/app/views/spree/checkout/_delivery.html.erb +++ b/core/app/views/spree/checkout/_delivery.html.erb @@ -6,11 +6,7 @@ <% @order.rate_hash.each do |shipping_method| %> <% end %>

diff --git a/core/app/views/spree/checkout/_summary.html.erb b/core/app/views/spree/checkout/_summary.html.erb index 1dfd612fde9..dd41c7daacb 100644 --- a/core/app/views/spree/checkout/_summary.html.erb +++ b/core/app/views/spree/checkout/_summary.html.erb @@ -4,27 +4,27 @@ <%= t(:item_total) %>: - <%= number_to_currency order.item_total %> + <%= money order.item_total %> <% order.adjustments.eligible.each do |adjustment| %> <% next if (adjustment.originator_type == 'Spree::TaxRate') and (adjustment.amount == 0) %> <%= adjustment.label %>: - <%= number_to_currency adjustment.amount %> + <%= money adjustment.amount %> <% end %> <%= t(:order_total) %>: - <%= number_to_currency @order.total %> + <%= money @order.total %> <% if order.price_adjustment_totals.present? %> <% @order.price_adjustment_totals.keys.each do |key| %> <%= key %> - <%= number_to_currency @order.price_adjustment_totals[key] %> + <%= money @order.price_adjustment_totals[key] %> <% end %> diff --git a/core/app/views/spree/checkout/edit.html.erb b/core/app/views/spree/checkout/edit.html.erb index ed5339cc0c5..3b6c35b20a6 100644 --- a/core/app/views/spree/checkout/edit.html.erb +++ b/core/app/views/spree/checkout/edit.html.erb @@ -12,6 +12,12 @@
<%= form_for @order, :url => update_checkout_path(@order.state), :html => { :id => "checkout_form_#{@order.state}" } do |form| %> + <% unless @order.email? %> +

+ <%= form.label :email %>
+ <%= form.text_field :email %> +

+ <% end %> <%= render @order.state, :form => form %> <% end %>
diff --git a/core/app/views/spree/checkout/payment/_gateway.html.erb b/core/app/views/spree/checkout/payment/_gateway.html.erb index d282c231360..fc4fd319739 100644 --- a/core/app/views/spree/checkout/payment/_gateway.html.erb +++ b/core/app/views/spree/checkout/payment/_gateway.html.erb @@ -19,7 +19,7 @@ <%= select_year(Date.today, :prefix => param_prefix, :field_name => 'year', :start_year => Date.today.year, :end_year => Date.today.year + 15, :class => 'required') %> *

-

+

<%= label_tag nil, t(:card_code) %>
<%= text_field_tag "#{param_prefix}[verification_value]", '', options_hash.merge(:id => 'card_code', :class => 'required', :size => 5) %> * diff --git a/core/app/views/spree/order_mailer/cancel_email.text.erb b/core/app/views/spree/order_mailer/cancel_email.text.erb index bc0171a1ff5..6ee3ae03fbe 100755 --- a/core/app/views/spree/order_mailer/cancel_email.text.erb +++ b/core/app/views/spree/order_mailer/cancel_email.text.erb @@ -1,16 +1,16 @@ -Dear Customer, +<%= t('order_mailer.cancel_email.dear_customer') %> -Your order has been CANCELED. Please retain this cancellation information for your records. +<%= t('order_mailer.cancel_email.instructions') %> ============================================================ -Order Summary [CANCELED] +<%= t('order_mailer.cancel_email.order_summary_canceled') %> ============================================================ <% @order.line_items.each do |item| %> - <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= number_to_currency item.price %> = <%= number_to_currency(item.price * item.quantity) %> + <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= money item.price %> = <%= money(item.price * item.quantity) %> <% end %> ============================================================ -Subtotal: <%= number_to_currency @order.item_total %> +<%= t('order_mailer.cancel_email.subtotal') %> <%= money @order.item_total %> <% @order.adjustments.eligible.each do |adjustment| %> - <%= raw(adjustment.label) %> <%= number_to_currency(adjustment.amount) %> + <%= raw(adjustment.label) %> <%= money(adjustment.amount) %> <% end %> -Order Total: <%= number_to_currency(@order.total) %> +<%= t('order_mailer.cancel_email.total') %> <%= money(@order.total) %> diff --git a/core/app/views/spree/order_mailer/confirm_email.text.erb b/core/app/views/spree/order_mailer/confirm_email.text.erb index 43d31452fca..a868b5250ff 100644 --- a/core/app/views/spree/order_mailer/confirm_email.text.erb +++ b/core/app/views/spree/order_mailer/confirm_email.text.erb @@ -1,19 +1,20 @@ -Dear Customer, +<%= t('order_mailer.confirm_email.dear_customer') %> -Please review and retain the following order information for your records. +<%= t('order_mailer.confirm_email.instructions') %> ============================================================ -Order Summary +<%= t('order_mailer.confirm_email.order_summary') %> ============================================================ <% @order.line_items.each do |item| %> - <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= number_to_currency item.price %> = <%= number_to_currency(item.price * item.quantity) %> + <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= money item.price %> = <%= money(item.price * item.quantity) %> <% end %> ============================================================ -Subtotal: <%= number_to_currency @order.item_total %> +<%= t('order_mailer.confirm_email.subtotal') %>: <%= money @order.item_total %> + <% @order.adjustments.eligible.each do |adjustment| %> - <%= raw(adjustment.label) %> <%= number_to_currency(adjustment.amount) %> + <%= raw(adjustment.label) %> <%= money(adjustment.amount) %> <% end %> -Order Total: <%= number_to_currency(@order.total) %> +<%= t('order_mailer.confirm_email.total') %>: <%= money(@order.total) %> -Thank you for your business. +<%= t('order_mailer.confirm_email.thanks') %> diff --git a/core/app/views/spree/orders/_adjustments.html.erb b/core/app/views/spree/orders/_adjustments.html.erb index ad6d8156ea1..c913661f2be 100644 --- a/core/app/views/spree/orders/_adjustments.html.erb +++ b/core/app/views/spree/orders/_adjustments.html.erb @@ -1,13 +1,13 @@ - Order Adjustments + <%= t(:order_adjustments) %> <% @order.adjustments.eligible.each do |adjustment| %> <%= adjustment.label %> - <%= number_to_currency(adjustment.amount) %> + <%= money(adjustment.amount) %> <% end %> diff --git a/core/app/views/spree/orders/_line_item.html.erb b/core/app/views/spree/orders/_line_item.html.erb index 0a34f06ea54..91fc7e6e7e4 100644 --- a/core/app/views/spree/orders/_line_item.html.erb +++ b/core/app/views/spree/orders/_line_item.html.erb @@ -17,13 +17,13 @@ <%= line_item_description(variant) %> - <%= number_to_currency line_item.price %> + <%= money line_item.price %> <%= item_form.number_field :quantity, :min => 0, :class => "line_item_quantity", :size => 5 %> - <%= number_to_currency(line_item.price * line_item.quantity) unless line_item.quantity.nil? %> + <%= money(line_item.price * line_item.quantity) unless line_item.quantity.nil? %> <%= link_to image_tag('icons/delete.png'), '#', :class => 'delete', :id => "delete_#{dom_id(line_item)}" %> diff --git a/core/app/views/spree/orders/edit.html.erb b/core/app/views/spree/orders/edit.html.erb index ba057fd8823..3e8393fb0eb 100644 --- a/core/app/views/spree/orders/edit.html.erb +++ b/core/app/views/spree/orders/edit.html.erb @@ -19,14 +19,16 @@

-
<%= t(:subtotal) %>: <%= order_subtotal(@order) %>
+
<%= t(:subtotal) %>: <%= @order.display_total %>
diff --git a/core/app/views/spree/products/_cart_form.html.erb b/core/app/views/spree/products/_cart_form.html.erb index 8f0723241ee..4dd465faab6 100644 --- a/core/app/views/spree/products/_cart_form.html.erb +++ b/core/app/views/spree/products/_cart_form.html.erb @@ -1,4 +1,4 @@ -<%= form_for :order, :url => populate_orders_url do |f| %> +<%= form_for :order, :url => populate_orders_path do |f| %>
<% if @product.has_variants? %> @@ -31,13 +31,13 @@
<%= t(:price) %>
-
<%= number_to_currency @product.price %>
+
<%= money @product.price %>
- <% if @product.has_stock? || Spree::Config[:allow_backorders] %> + <% if @product.on_sale? %> <%= number_field_tag (@product.has_variants? ? :quantity : "variants[#{@product.master.id}]"), - 1, :class => 'title', :in => 1..@product.on_hand %> + 1, :class => 'title', :in => 1..@product.on_hand, :min => 1 %> <%= button_tag :class => 'large primary', :id => 'add-to-cart-button', :type => :submit do %> <%= t(:add_to_cart) %> <% end %> diff --git a/core/app/views/spree/products/_thumbnails.html.erb b/core/app/views/spree/products/_thumbnails.html.erb index f0013afae47..a1d471db7c7 100644 --- a/core/app/views/spree/products/_thumbnails.html.erb +++ b/core/app/views/spree/products/_thumbnails.html.erb @@ -1,5 +1,5 @@ -<% if product.images.size > 1 %> +<% if product.images.size > 1 || product.variant_images.size > 0 %>
    <% product.images.each do |i| %>
  • <%= link_to image_tag(i.attachment.url(:mini)), i.attachment.url(:product) %>
  • diff --git a/core/app/views/spree/shared/_google_analytics.html.erb b/core/app/views/spree/shared/_google_analytics.html.erb index 5436557f24e..03207eaa50a 100644 --- a/core/app/views/spree/shared/_google_analytics.html.erb +++ b/core/app/views/spree/shared/_google_analytics.html.erb @@ -6,27 +6,28 @@ _gaq.push(['_trackPageview']); <% if flash[:commerce_tracking] %> - // report e-commerce transaction information when applicable + <%# more info: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce %> _gaq.push(['_addTrans', - "<%= @order.number %>", // Order Number - "", // Affiliation - "<%= @order.total %>", // Order total - "<%= @order.adjustments.tax.sum(:amount) %>", // Tax Amount - "<%= @order.adjustments.shipping.sum(:amount) %>", // Ship Amount - "", // City - "", // State - "" // Country + "<%= @order.number %>", + "", + "<%= @order.total %>", + "<%= @order.adjustments.tax.sum(:amount) %>", + "<%= @order.adjustments.shipping.sum(:amount) %>", + "<%= @order.bill_address.city %>", + "<%= @order.bill_address.state_text %>", + "<%= @order.bill_address.country.name %>" ]); <% @order.line_items.each do |line_item| %> _gaq.push(['_addItem', - "<%= @order.number %>", // order ID - required - "<%= line_item.variant.sku %>", // SKU/code - required - "<%= line_item.variant.product.name %>", // product name - "", // category or variation, Product Category - "<%= line_item.price %>", // unit price - required - "<%= line_item.quantity %>" // quantity - required + "<%= @order.number %>", + "<%= line_item.variant.sku %>", + "<%= line_item.variant.product.name %>", + "", + "<%= line_item.price %>", + "<%= line_item.quantity %>" ]); <% end %> + _gaq.push(['_trackTrans']); <% end %> (function() { diff --git a/core/app/views/spree/shared/_head.html.erb b/core/app/views/spree/shared/_head.html.erb index ae107c4a5ab..86d91408538 100644 --- a/core/app/views/spree/shared/_head.html.erb +++ b/core/app/views/spree/shared/_head.html.erb @@ -1,6 +1,4 @@ - - <%= title %> diff --git a/core/app/views/spree/shared/_order_details.html.erb b/core/app/views/spree/shared/_order_details.html.erb index 8f870a52459..97a7aab6e17 100644 --- a/core/app/views/spree/shared/_order_details.html.erb +++ b/core/app/views/spree/shared/_order_details.html.erb @@ -1,26 +1,28 @@
    - -
    -
    <%= t(:shipping_address) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? %>
    -
    - <%= order.ship_address %> -
    -
    -
    -
    <%= t(:billing_address) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? %>
    -
    - <%= order.bill_address %> + <% if order.has_step?("address") %> +
    +
    <%= t(:shipping_address) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? %>
    +
    + <%= order.ship_address %> +
    -
    - <% if @order.has_step?("delivery") %>
    -
    <%= t(:shipping_method) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:delivery) unless @order.completed? %>
    -
    - <%= order.shipping_method.name %> +
    <%= t(:billing_address) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? %>
    +
    + <%= order.bill_address %>
    + + <% if @order.has_step?("delivery") %> +
    +
    <%= t(:shipping_method) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:delivery) unless @order.completed? %>
    +
    + <%= order.shipping_method.name %> +
    +
    + <% end %> <% end %>
    @@ -73,18 +75,18 @@

    <%= item.variant.product.name %>

    <%= truncate(item.variant.product.description, :length => 100, :omission => "...") %> - <%= "(" + variant_options(item.variant) + ")" unless item.variant .option_values.empty? %> + <%= "(" + item.variant.options_text + ")" unless item.variant.option_values.empty? %> - <%= number_to_currency item.price %> + <%= money item.price %> <%= item.quantity %> - <%= number_to_currency (item.price * item.quantity) %> + <%= money(item.price * item.quantity) %> <% end %> <%= t(:order_total) %>: - <%= number_to_currency @order.total %> + <%= money @order.total %> <% if order.price_adjustment_totals.present? %> @@ -92,7 +94,7 @@ <% @order.price_adjustment_totals.keys.each do |key| %> <%= key %> - <%= number_to_currency @order.price_adjustment_totals[key] %> + <%= money @order.price_adjustment_totals[key] %> <% end %> @@ -100,7 +102,7 @@ <%= t(:subtotal) %>: - <%= number_to_currency @order.item_total %> + <%= money @order.item_total %> @@ -108,7 +110,7 @@ <% next if (adjustment.originator_type == 'Spree::TaxRate') and (adjustment.amount == 0) %> <%= adjustment.label %> - <%= number_to_currency adjustment.amount %> + <%= money adjustment.amount %> <% end %> diff --git a/core/app/views/spree/shared/_products.html.erb b/core/app/views/spree/shared/_products.html.erb index 3dec40511ff..b126dcbc01f 100644 --- a/core/app/views/spree/shared/_products.html.erb +++ b/core/app/views/spree/shared/_products.html.erb @@ -12,13 +12,13 @@
      <% reset_cycle('default') %> <% products.each do |product| %> - <% if Spree::Config[:show_zero_stock_products] || product.has_stock? %> + <% if product.on_display? %>
    • "classes") %>" data-hook="products_list_item" itemscope itemtype="http://schema.org/Product">
      <%= link_to small_image(product, :itemprop => "image"), product, :itemprop => 'url' %>
      <%= link_to truncate(product.name, :length => 50), product, :class => 'info', :itemprop => "name", :title => product.name %> - <%= number_to_currency product.price %> + <%= product.display_price %>
    • <% end %> <% end %> diff --git a/core/app/views/spree/shared/_taxonomies.html.erb b/core/app/views/spree/shared/_taxonomies.html.erb index dbccfb6eaa6..512d39fdf68 100644 --- a/core/app/views/spree/shared/_taxonomies.html.erb +++ b/core/app/views/spree/shared/_taxonomies.html.erb @@ -1,6 +1,6 @@ diff --git a/core/app/views/spree/shipment_mailer/shipped_email.text.erb b/core/app/views/spree/shipment_mailer/shipped_email.text.erb index bd6b30f1c62..76a95577742 100644 --- a/core/app/views/spree/shipment_mailer/shipped_email.text.erb +++ b/core/app/views/spree/shipment_mailer/shipped_email.text.erb @@ -1,15 +1,15 @@ -Dear Customer, +<%= t('shipment_mailer.shipped_email.dear_customer') %> -Your order has been shipped +<%= t('shipment_mailer.shipped_email.instructions') %> ============================================================ -Shipment Summary +<%= t('shipment_mailer.shipped_email.shipment_summary') %> ============================================================ -<% @shipment.line_items.each do |item| %> - <%= item.variant.sku %> <%= item.variant.product.name %> <%= variant_options(item.variant, :include_style => false) %> (<%= item.quantity %>) +<% @shipment.manifest.each do |item| %> + <%= item.variant.sku %> <%= item.variant.product.name %> <%= item.variant.options_text %> <% end %> ============================================================ -<%= "Tracking Information: #{@shipment.tracking}" if @shipment.tracking %> +<%= t('shipment_mailer.shipped_email.track_information', :tracking => @shipment.tracking) if @shipment.tracking %> -Thank you for your business. +<%= t('shipment_mailer.shipped_email.thanks') %> diff --git a/core/config/initializers/check_for_orphaned_preferences.rb b/core/config/initializers/check_for_orphaned_preferences.rb index c0311dc4b62..5a3204bd249 100644 --- a/core/config/initializers/check_for_orphaned_preferences.rb +++ b/core/config/initializers/check_for_orphaned_preferences.rb @@ -1,5 +1,5 @@ begin - ActiveRecord::Base.connection.execute("select owner_id, owner_type, name, value from spree_preferences where `key` is null").each do |pref| + ActiveRecord::Base.connection.execute("SELECT owner_id, owner_type, name, value FROM spree_preferences WHERE 'key' IS NULL").each do |pref| warn "[WARNING] Orphaned preference `#{pref[2]}` with value `#{pref[3]}` for #{pref[1]} with id of: #{pref[0]}, you should reset the preference value manually." end rescue diff --git a/core/config/initializers/rails_5868.rb b/core/config/initializers/rails_5868.rb new file mode 100644 index 00000000000..77eeaee3d4f --- /dev/null +++ b/core/config/initializers/rails_5868.rb @@ -0,0 +1,8 @@ +# Temporary fix for +# NoMethodError: undefined method `gsub' for # +# See https://github.com/rails/rails/issues/5868 +class Arel::Nodes::Ordering + def gsub(*a, &b) + to_sql.gsub(*a, &b) + end +end diff --git a/core/config/initializers/workarounds_for_ruby19.rb b/core/config/initializers/workarounds_for_ruby19.rb deleted file mode 100644 index 34210c07d54..00000000000 --- a/core/config/initializers/workarounds_for_ruby19.rb +++ /dev/null @@ -1,72 +0,0 @@ -# encoding: utf-8 -if RUBY_VERSION.to_f >= 1.9 - - class String - def mb_chars - self.force_encoding(Encoding::UTF_8) - end - - alias_method(:orig_concat, :concat) - def concat(value) - orig_concat value.frozen? || !value.respond_to?(:force_encoding) ? value : value.force_encoding(Encoding::UTF_8) - end - end - - module ActionView - module Renderable #:nodoc: - private - def compile!(render_symbol, local_assigns) - locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join - - source = <<-end_src - def #{render_symbol}(local_assigns) - old_output_buffer = output_buffer;#{locals_code};#{compiled_source} - ensure - self.output_buffer = old_output_buffer - end - end_src - source.force_encoding(Encoding::UTF_8) if source.respond_to?(:force_encoding) - - begin - ActionView::Base::CompiledTemplates.module_eval(source, filename, 0) - rescue Errno::ENOENT => e - raise e # Missing template file, re-raise for Base to rescue - rescue Exception => e # errors from template code - if logger = defined?(ActionController) && Base.logger - logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" - logger.debug "Function body: #{source}" - logger.debug "Backtrace: #{e.backtrace.join("\n")}" - end - - raise ActionView::TemplateError.new(self, {}, e) - end - end - end - end - - if defined?(Mysql::Result) - class Mysql::Result - def encode(value, encoding = "utf-8") - String === value ? value.force_encoding(encoding) : value - end - - def each_utf8(&block) - each_orig do |row| - yield row.map {|col| encode(col) } - end - end - alias each_orig each - alias each each_utf8 - - def each_hash_utf8(&block) - each_hash_orig do |row| - row.each {|k, v| row[k] = encode(v) } - yield(row) - end - end - alias each_hash_orig each_hash - alias each_hash each_hash_utf8 - end - end - -end diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index 74178bc7b31..7e4392da26c 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -108,6 +108,7 @@ en: spree/tax_rate: amount: Rate included_in_price: Included in Price + show_rate_in_label: Show rate in label spree/taxon: name: Name permalink: Permalink @@ -334,11 +335,14 @@ en: credit_cards: Credit Cards credits: Credits current: Current + currency: Currency + currency_symbol_position: "Put currency symbol before or after dollar amount?" customer: Customer customer_details: "Customer Details" customer_details_updated: "The customer's details have been updated." customer_search: "Customer Search" date_created: Date created + date_completed: Date Completed date_range: "Date Range" debit: Debit default: Default @@ -357,7 +361,9 @@ en: didnt_receive_unlock_instructions: "Didn't receive unlock instructions?" discount_amount: "Discount Amount" display: Display + display_currency: "Display currency" dismiss_banner: "No. Thanks! I'm not interested, do not display this message again" + dollar_amounts_displayed_as: "Dollar amounts displayed as %{example}" edit: Edit editing_billing_integration: Editing Billing Integration editing_category: "Editing Category" @@ -599,6 +605,7 @@ en: or: or or_over_price: "%{price} or over" order: Order + order_adjustments: "Order adjustments" order_confirmation_note: "" order_date: "Order Date" order_details: "Order Details" @@ -606,8 +613,19 @@ en: order_mailer: confirm_email: subject: "Order Confirmation" + dear_customer: "Dear Customer," + instructions: "Please review and retain the following order information for your records." + order_summary: "Order Summary" + subtotal: "Subtotal:" + total: "Order Total:" + thanks: "Thank you for your business." cancel_email: subject: "Cancellation of Order" + dear_customer: "Dear Customer," + instructions: "Your order has been CANCELED. Please retain this cancellation information for your records." + order_summary_canceled: "Order Summary [CANCELED]" + subtotal: "Subtotal:" + total: "Order Total:" order_not_in_system: That order number is not valid on this site. order_number: Order order_operation_authorize: Authorize @@ -682,6 +700,7 @@ en: please_create_user: "Please create a user account" please_define_payment_methods: "Please define some payment methods first." powered_by: "Powered by" + populate_get_error: "Something went wrong. Please try adding the item again." presentation: Presentation preview: Preview previous: Previous @@ -896,6 +915,11 @@ en: shipment_mailer: shipped_email: subject: "Shipment Notification" + dear_customer: "Dear Customer," + instructions: "Your order has been shipped" + shipment_summary: "Shipment Summary" + track_information: "Tracking Information: %{tracking}" + thanks: "Thank you for your business." shipment_number: "Shipment #" shipment_state: Shipment State shipment_states: @@ -929,6 +953,7 @@ en: show_incomplete_orders: "Show Incomplete Orders" show_only_complete_orders: "Only show complete orders" show_out_of_stock_products: "Show out-of-stock products" + show_only_unfulfilled_orders: "Show only unfulfilled orders" showing_first_n: "Showing first %{n}" sign_up: "Sign up" site_name: "Site Name" diff --git a/core/config/routes.rb b/core/config/routes.rb index 2f88478eef9..48ff4c500bc 100755 --- a/core/config/routes.rb +++ b/core/config/routes.rb @@ -13,7 +13,14 @@ # non-restful checkout stuff put '/checkout/update/:state', :to => 'checkout#update', :as => :update_checkout get '/checkout/:state', :to => 'checkout#edit', :as => :checkout_state - get '/checkout', :to => 'checkout#edit', :state => 'address', :as => :checkout + get '/checkout', :to => 'checkout#edit' , :as => :checkout + + populate_redirect = redirect do |params, request| + request.flash[:error] = I18n.t(:populate_get_error) + request.referer || '/cart' + end + + get '/orders/populate', :via => :get, :to => populate_redirect resources :orders do post :populate, :on => :collection @@ -143,6 +150,12 @@ resources :taxons end + resources :taxons, :only => [] do + collection do + get :search + end + end + resources :reports, :only => [:index, :show] do collection do get :sales_total diff --git a/core/db/default/spree/countries.yml b/core/db/default/spree/countries.yml index b74cd01f46a..875cca816e0 100644 --- a/core/db/default/spree/countries.yml +++ b/core/db/default/spree/countries.yml @@ -1581,3 +1581,9 @@ countries_171: iso_name: SAINT KITTS AND NEVIS id: "171" numcode: "659" +countries_998: + name: Serbia + iso3: SRB + iso: RS + id: "998" + numcode: "999" \ No newline at end of file diff --git a/core/db/migrate/20120905145253_add_tax_rate_label.rb b/core/db/migrate/20120905145253_add_tax_rate_label.rb new file mode 100644 index 00000000000..9c70082973a --- /dev/null +++ b/core/db/migrate/20120905145253_add_tax_rate_label.rb @@ -0,0 +1,5 @@ +class AddTaxRateLabel < ActiveRecord::Migration + def change + add_column :spree_tax_rates, :name, :string + end +end diff --git a/core/db/migrate/20120905151823_add_toggle_tax_rate_display.rb b/core/db/migrate/20120905151823_add_toggle_tax_rate_display.rb new file mode 100644 index 00000000000..f4f3406c3ef --- /dev/null +++ b/core/db/migrate/20120905151823_add_toggle_tax_rate_display.rb @@ -0,0 +1,5 @@ +class AddToggleTaxRateDisplay < ActiveRecord::Migration + def change + add_column :spree_tax_rates, :show_rate_in_label, :boolean, :default => true + end +end diff --git a/core/db/migrate/20121009142519_add_lock_version_to_variant.rb b/core/db/migrate/20121009142519_add_lock_version_to_variant.rb new file mode 100644 index 00000000000..91c8ec33a7f --- /dev/null +++ b/core/db/migrate/20121009142519_add_lock_version_to_variant.rb @@ -0,0 +1,5 @@ +class AddLockVersionToVariant < ActiveRecord::Migration + def change + add_column :spree_variants, :lock_version, :integer, :default => 0 + end +end diff --git a/core/db/migrate/20121017010007_remove_not_null_constraint_from_products_on_hand.rb b/core/db/migrate/20121017010007_remove_not_null_constraint_from_products_on_hand.rb new file mode 100644 index 00000000000..8f413e325f8 --- /dev/null +++ b/core/db/migrate/20121017010007_remove_not_null_constraint_from_products_on_hand.rb @@ -0,0 +1,11 @@ +class RemoveNotNullConstraintFromProductsOnHand < ActiveRecord::Migration + def up + change_column :spree_products, :count_on_hand, :integer, :null => true + change_column :spree_variants, :count_on_hand, :integer, :null => true + end + + def down + change_column :spree_products, :count_on_hand, :integer, :null => false + change_column :spree_variants, :count_on_hand, :integer, :null => false + end +end diff --git a/core/lib/generators/spree/custom_user/custom_user_generator.rb b/core/lib/generators/spree/custom_user/custom_user_generator.rb index cb261d0edc4..8c9be8278c1 100644 --- a/core/lib/generators/spree/custom_user/custom_user_generator.rb +++ b/core/lib/generators/spree/custom_user/custom_user_generator.rb @@ -27,7 +27,10 @@ def generate file_action = File.exist?('config/initializers/spree.rb') ? :append_file : :create_file send(file_action, 'config/initializers/spree.rb') do - %Q{require 'spree/authentication_helpers'\n} + %Q{ + Rails.application.config.to_prepare do + require_dependency 'spree/authentication_helpers' + end\n} end end diff --git a/core/lib/generators/spree/install/install_generator.rb b/core/lib/generators/spree/install/install_generator.rb index 9d7d8731fbf..0727f77aabc 100644 --- a/core/lib/generators/spree/install/install_generator.rb +++ b/core/lib/generators/spree/install/install_generator.rb @@ -9,6 +9,7 @@ class InstallGenerator < Rails::Generators::Base class_option :seed, :type => :boolean, :default => true, :banner => 'load seed data (migrations must be run)' class_option :sample, :type => :boolean, :default => true, :banner => 'load sample data (migrations must be run)' class_option :auto_accept, :type => :boolean + class_option :user_class, :type => :string class_option :admin_email, :type => :string class_option :admin_password, :type => :string class_option :lib_name, :type => :string, :default => 'spree' diff --git a/core/lib/generators/spree/install/templates/config/initializers/spree.rb b/core/lib/generators/spree/install/templates/config/initializers/spree.rb index 3c8249af012..c00d49f2faf 100644 --- a/core/lib/generators/spree/install/templates/config/initializers/spree.rb +++ b/core/lib/generators/spree/install/templates/config/initializers/spree.rb @@ -11,4 +11,4 @@ # config.site_name = "Spree Demo Site" end -Spree.user_class = "Spree::LegacyUser" +Spree.user_class = <%= (options[:user_class].blank? ? "Spree::LegacyUser" : options[:user_class]).inspect %> diff --git a/core/lib/spree/core.rb b/core/lib/spree/core.rb index ddecf60101b..c962bc8ebdb 100644 --- a/core/lib/spree/core.rb +++ b/core/lib/spree/core.rb @@ -31,7 +31,7 @@ require 'state_machine' require 'paperclip' require 'kaminari' -require 'nested_set' +require 'awesome_nested_set' require 'acts_as_list' require 'active_merchant' require 'ransack' @@ -39,6 +39,8 @@ require 'deface' require 'cancan' require 'select2-rails' +require 'spree/money' +require 'rabl' module Spree @@ -104,7 +106,7 @@ def self.config(&block) end if defined?(ActionView) - require 'nested_set/helper' + require 'awesome_nested_set/helper' ActionView::Base.class_eval do include CollectiveIdea::Acts::NestedSet::Helper end diff --git a/core/lib/spree/core/controller_helpers.rb b/core/lib/spree/core/controller_helpers.rb index ef7037a3575..12877a00f3e 100644 --- a/core/lib/spree/core/controller_helpers.rb +++ b/core/lib/spree/core/controller_helpers.rb @@ -48,6 +48,24 @@ def title end end + def associate_user + if try_spree_current_user && @order + if @order.user.blank? || @order.email.blank? + @order.associate_user!(try_spree_current_user) + end + end + + # This will trigger any "first order" promotions to be triggered + # Assuming of course that this session variable was set correctly in + # the authentication provider's registrations controller + if session[:spree_user_signup] + fire_event('spree.user.signup', :user => try_spree_current_user, :order => current_order(true)) + end + + session[:guest_token] = nil + session[:spree_user_signup] = nil + end + protected def set_current_order @@ -89,7 +107,7 @@ def unauthorized format.html do if try_spree_current_user flash.now[:error] = t(:authorization_failure) - render 'spree/shared/unauthorized', :layout => '/spree/layouts/spree_application', :status => 401 + render 'spree/shared/unauthorized', :layout => Spree::Config[:layout], :status => 401 else store_location url = respond_to?(:spree_login_path) ? spree_login_path : root_path diff --git a/core/lib/spree/core/engine.rb b/core/lib/spree/core/engine.rb index 09014497a00..e9b2589e044 100644 --- a/core/lib/spree/core/engine.rb +++ b/core/lib/spree/core/engine.rb @@ -10,7 +10,6 @@ class Engine < ::Rails::Engine config.autoload_paths += %W(#{config.root}/lib) def self.activate - Spree::Order.define_state_machine! end config.to_prepare &method(:activate).to_proc diff --git a/core/lib/spree/core/mail_settings.rb b/core/lib/spree/core/mail_settings.rb index 27d6a7b3004..6badb9e4770 100644 --- a/core/lib/spree/core/mail_settings.rb +++ b/core/lib/spree/core/mail_settings.rb @@ -21,7 +21,8 @@ def self.init mail_server_settings[:password] = mail_method.preferred_smtp_password end - mail_server_settings[:enable_starttls_auto] = (mail_method.preferred_secure_connection_type == 'TLS') + tls = mail_method.preferred_secure_connection_type == 'TLS' + mail_server_settings[:enable_starttls_auto] = tls ActionMailer::Base.smtp_settings = mail_server_settings ActionMailer::Base.perform_deliveries = true diff --git a/core/lib/spree/core/permalinks.rb b/core/lib/spree/core/permalinks.rb index fb8a2b45416..094223cfcce 100644 --- a/core/lib/spree/core/permalinks.rb +++ b/core/lib/spree/core/permalinks.rb @@ -39,8 +39,7 @@ def permalink_order end end - def save_permalink - permalink_value = self.to_param + def save_permalink(permalink_value=self.to_param) field = self.class.permalink_field # Do other links exist with this permalink? other = self.class.where("#{field} LIKE ?", "#{permalink_value}%") diff --git a/core/lib/spree/core/relation_serialization.rb b/core/lib/spree/core/relation_serialization.rb deleted file mode 100644 index d6ade62254f..00000000000 --- a/core/lib/spree/core/relation_serialization.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Spree - module Core - module RelationSerialization - def serializable_hash(options = nil) - self.map { |a| a.serializable_hash(options) } - end - end - end -end \ No newline at end of file diff --git a/core/lib/spree/core/search/base.rb b/core/lib/spree/core/search/base.rb index 6cbfffa6002..87b6423f6c4 100644 --- a/core/lib/spree/core/search/base.rb +++ b/core/lib/spree/core/search/base.rb @@ -3,6 +3,7 @@ module Core module Search class Base attr_accessor :properties + attr_accessor :current_user def initialize(params) @properties = {} @@ -28,7 +29,7 @@ def method_missing(name) def get_base_scope base_scope = Spree::Product.active base_scope = base_scope.in_taxon(taxon) unless taxon.blank? - base_scope = get_products_conditions_for(base_scope, keywords) unless keywords.blank? + base_scope = get_products_conditions_for(base_scope, keywords) base_scope = base_scope.on_hand unless Spree::Config[:show_zero_stock_products] base_scope = add_search_scopes(base_scope) base_scope @@ -48,7 +49,10 @@ def add_search_scopes(base_scope) # method should return new scope based on base_scope def get_products_conditions_for(base_scope, query) - base_scope.like_any([:name, :description], query.split) + unless query.blank? + base_scope = base_scope.like_any([:name, :description], query.split) + end + base_scope end def prepare(params) diff --git a/promo/spec/support/authorization_helpers.rb b/core/lib/spree/core/testing_support/authorization_helpers.rb similarity index 99% rename from promo/spec/support/authorization_helpers.rb rename to core/lib/spree/core/testing_support/authorization_helpers.rb index 48fd74518a2..cb40b9c3bc0 100644 --- a/promo/spec/support/authorization_helpers.rb +++ b/core/lib/spree/core/testing_support/authorization_helpers.rb @@ -27,4 +27,4 @@ def stub_authorization! RSpec.configure do |config| config.extend AuthorizationHelpers::Controller, :type => :controller config.extend AuthorizationHelpers::Request, :type => :request -end +end \ No newline at end of file diff --git a/core/lib/spree/core/testing_support/common_rake.rb b/core/lib/spree/core/testing_support/common_rake.rb index ec560a0d61b..54400382eb9 100644 --- a/core/lib/spree/core/testing_support/common_rake.rb +++ b/core/lib/spree/core/testing_support/common_rake.rb @@ -4,11 +4,12 @@ desc "Generates a dummy app for testing" namespace :common do - task :test_app do + task :test_app, :user_class do |t, args| + args.with_defaults(:user_class => "Spree::LegacyUser") require "#{ENV['LIB_NAME']}" Spree::DummyGenerator.start ["--lib_name=#{ENV['LIB_NAME']}", "--database=#{ENV['DB_NAME']}", "--quiet"] - Spree::InstallGenerator.start ["--lib_name=#{ENV['LIB_NAME']}", "--auto-accept", "--migrate=false", "--seed=false", "--sample=false", "--quiet"] + Spree::InstallGenerator.start ["--lib_name=#{ENV['LIB_NAME']}", "--auto-accept", "--migrate=false", "--seed=false", "--sample=false", "--quiet", "--user_class=#{args[:user_class]}"] puts "Setting up dummy database..." cmd = "bundle exec rake db:drop db:create db:migrate db:test:prepare" diff --git a/core/lib/spree/core/testing_support/env.rb b/core/lib/spree/core/testing_support/env.rb deleted file mode 100644 index 16bae913543..00000000000 --- a/core/lib/spree/core/testing_support/env.rb +++ /dev/null @@ -1,2 +0,0 @@ -Capybara.default_selector = :css -Capybara.server_boot_timeout = 50 \ No newline at end of file diff --git a/core/lib/spree/core/testing_support/factories/product_factory.rb b/core/lib/spree/core/testing_support/factories/product_factory.rb index 0794d827e09..8f13ecbe3b3 100644 --- a/core/lib/spree/core/testing_support/factories/product_factory.rb +++ b/core/lib/spree/core/testing_support/factories/product_factory.rb @@ -1,6 +1,6 @@ FactoryGirl.define do factory :simple_product, :class => Spree::Product do - sequence(:name) { |n| "Product ##{n} - #{rand(9999)}" } + sequence(:name) { |n| "Product ##{n} - #{Kernel.rand(9999)}" } description { Faker::Lorem.paragraphs(1 + Kernel.rand(5)).join("\n") } price 19.99 cost_price 17.00 diff --git a/core/lib/spree/core/testing_support/factories/user_factory.rb b/core/lib/spree/core/testing_support/factories/user_factory.rb index d022da3d48f..ede76b713df 100644 --- a/core/lib/spree/core/testing_support/factories/user_factory.rb +++ b/core/lib/spree/core/testing_support/factories/user_factory.rb @@ -3,12 +3,12 @@ "xxxx#{Time.now.to_i}#{rand(1000)}#{n}xxxxxxxxxxxxx" end - factory :user, :class => Spree::LegacyUser do + factory :user, :class => Spree.user_class do email { Faker::Internet.email } login { email } password 'secret' password_confirmation 'secret' - authentication_token { FactoryGirl.generate(:user_authentication_token) } if Spree::LegacyUser.attribute_method? :authentication_token + authentication_token { FactoryGirl.generate(:user_authentication_token) } if Spree.user_class.attribute_method? :authentication_token end factory :admin_user, :parent => :user do diff --git a/core/lib/spree/core/testing_support/flash.rb b/core/lib/spree/core/testing_support/flash.rb new file mode 100644 index 00000000000..6be2d4f0189 --- /dev/null +++ b/core/lib/spree/core/testing_support/flash.rb @@ -0,0 +1,17 @@ +module Spree + module Core + module TestingSupport + module Flash + def assert_flash_notice(flash) + if flash.is_a?(Symbol) + flash = I18n.t(flash) + end + + within("[class='flash notice']") do + page.should have_content(flash) + end + end + end + end + end +end diff --git a/core/lib/spree/core/testing_support/preferences.rb b/core/lib/spree/core/testing_support/preferences.rb new file mode 100644 index 00000000000..f2c2c94bc06 --- /dev/null +++ b/core/lib/spree/core/testing_support/preferences.rb @@ -0,0 +1,26 @@ +module Spree + module Core + module TestingSupport + module Preferences + # Resets all preferences to default values, you can + # pass a block to override the defaults with a block + # + # reset_spree_preferences do |config| + # config.site_name = "my fancy pants store" + # end + # + def reset_spree_preferences + Spree::Preferences::Store.instance.persistence = false + config = Rails.application.config.spree.preferences + config.reset + yield(config) if block_given? + end + + def assert_preference_unset(preference) + find("#preferences_#{preference}")['checked'].should be_false + Spree::Config[preference].should be_false + end + end + end + end +end diff --git a/core/lib/spree/core/version.rb b/core/lib/spree/core/version.rb index a7682f8a19d..3cab75d2453 100644 --- a/core/lib/spree/core/version.rb +++ b/core/lib/spree/core/version.rb @@ -1,5 +1,5 @@ module Spree def self.version - "1.2.0.rc1" + "1.2.2" end end diff --git a/core/lib/spree/money.rb b/core/lib/spree/money.rb new file mode 100644 index 00000000000..0160bfceb9d --- /dev/null +++ b/core/lib/spree/money.rb @@ -0,0 +1,19 @@ +require 'money' + +module Spree + class Money + def initialize(amount, options={}) + @money = ::Money.parse([amount, Spree::Config[:currency]].join) + @options = {} + @options[:with_currency] = true if Spree::Config[:display_currency] + @options[:symbol_position] = Spree::Config[:currency_symbol_position].to_sym + @options.merge!(options) + # Must be a symbol because the Money gem doesn't do the conversion + @options[:symbol_position] = @options[:symbol_position].to_sym + end + + def to_s + @money.format(@options) + end + end +end diff --git a/core/lib/spree/product_filters.rb b/core/lib/spree/product_filters.rb index a070e05908b..e25c435a3de 100644 --- a/core/lib/spree/product_filters.rb +++ b/core/lib/spree/product_filters.rb @@ -46,9 +46,6 @@ module Spree # happen until Taxon class is loaded. Ensure that Taxon class is loaded before # you try something like Product.price_range_any module ProductFilters - extend ActionView::Helpers::NumberHelper - extend Spree::BaseHelper - # Example: filtering by price # The named scope just maps incoming labels onto their conditions, and builds the conjunction # 'price' is in the base scope's context (ie, "select foo from products where ...") so @@ -67,6 +64,10 @@ module ProductFilters Spree::Product.joins(:master).where(scope) end + def ProductFilters.format_price(amount) + Spree::Money.new(amount) + end + def ProductFilters.price_filter v = Spree::Variant.arel_table conds = [ [ I18n.t(:under_price, :price => format_price(10)) , v[:price].lteq(10)], diff --git a/core/spec/controllers/spree/admin/image_settings_controller_spec.rb b/core/spec/controllers/spree/admin/image_settings_controller_spec.rb index 96f41a769fc..ab8d6a77dfe 100644 --- a/core/spec/controllers/spree/admin/image_settings_controller_spec.rb +++ b/core/spec/controllers/spree/admin/image_settings_controller_spec.rb @@ -29,6 +29,10 @@ end context "amazon s3" do + after(:all) do + Spree::Image.attachment_definitions[:attachment].delete :storage + end + it "should be able to update s3 settings" do spree_put :update, { :preferences => { "use_s3" => "1", diff --git a/core/spec/controllers/spree/admin/mail_methods_controller_spec.rb b/core/spec/controllers/spree/admin/mail_methods_controller_spec.rb index 923f2ada8b8..aaa494d0496 100644 --- a/core/spec/controllers/spree/admin/mail_methods_controller_spec.rb +++ b/core/spec/controllers/spree/admin/mail_methods_controller_spec.rb @@ -7,7 +7,6 @@ let(:mail_method) { mock_model(Spree::MailMethod).as_null_object } before do - Spree::Order.stub :find => order Spree::MailMethod.stub :find => mail_method request.env["HTTP_REFERER"] = "/" end @@ -15,14 +14,14 @@ context "#create" do it "should reinitialize the mail settings" do Spree::Core::MailSettings.should_receive :init - spree_put :create, {:order_id => "123", :id => "456", :mail_method_parmas => {:environment => "foo"}} + spree_put :create, { :id => "456", :mail_method => {:environment => "foo"}} end end context "#update" do it "should reinitialize the mail settings" do Spree::Core::MailSettings.should_receive :init - spree_put :update, {:order_id => "123", :id => "456", :mail_method_params => {:environment => "foo"}} + spree_put :update, { :id => "456", :mail_method => {:environment => "foo"}} end end diff --git a/core/spec/controllers/spree/admin/orders_controller_spec.rb b/core/spec/controllers/spree/admin/orders_controller_spec.rb index 3d98bcfc1cd..098db000c8f 100644 --- a/core/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/core/spec/controllers/spree/admin/orders_controller_spec.rb @@ -15,6 +15,7 @@ order.should_receive(:foo).and_return true spree_put :fire, {:id => "R1234567", :e => "foo"} end + it "should respond with a flash message if the event cannot be fired" do order.stub :foo => false spree_put :fire, {:id => "R1234567", :e => "foo"} @@ -22,4 +23,11 @@ end end + context "pagination" do + it "can page through the orders" do + spree_get :index, :page => 2, :per_page => 10 + assigns[:orders].offset_value.should == 10 + assigns[:orders].limit_value.should == 10 + end + end end diff --git a/core/spec/controllers/spree/admin/payment_methods_controller_spec.rb b/core/spec/controllers/spree/admin/payment_methods_controller_spec.rb new file mode 100644 index 00000000000..a1acff2e198 --- /dev/null +++ b/core/spec/controllers/spree/admin/payment_methods_controller_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +module Spree + class GatewayWithPassword < PaymentMethod + attr_accessible :preferred_password + + preference :password, :string, :default => "password" + end +end + +module Spree + describe Admin::PaymentMethodsController do + stub_authorization! + + let(:payment_method) { Spree::GatewayWithPassword.create!(:name => "Bogus", :preferred_password => "haxme") } + + # regression test for #2094 + it "does not clear password on update" do + payment_method.preferred_password.should == "haxme" + spree_put :update, :id => payment_method.id, :payment_method => { :type => payment_method.class.to_s, :preferred_password => "" } + response.should redirect_to(spree.edit_admin_payment_method_path(payment_method)) + + payment_method.reload + payment_method.preferred_password.should == "haxme" + end + + end +end diff --git a/core/spec/controllers/spree/home_controller_spec.rb b/core/spec/controllers/spree/home_controller_spec.rb new file mode 100644 index 00000000000..b5342dde718 --- /dev/null +++ b/core/spec/controllers/spree/home_controller_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe Spree::HomeController do + it "should provide the current user to the searcher class" do + user = stub(:last_incomplete_spree_order => nil) + controller.stub :spree_current_user => user + Spree::Config.searcher_class.any_instance.should_receive(:current_user=).with(user) + spree_get :index + response.status.should == 200 + end +end diff --git a/core/spec/controllers/spree/orders_controller_transitions_spec.rb b/core/spec/controllers/spree/orders_controller_transitions_spec.rb new file mode 100644 index 00000000000..4d24b7952ed --- /dev/null +++ b/core/spec/controllers/spree/orders_controller_transitions_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +Spree::Order.class_eval do + attr_accessor :did_transition +end + +module Spree + describe OrdersController do + # Regression test for #2004 + context "with a transition callback on first state" do + let(:order) { Spree::Order.new } + + before do + controller.stub :current_order => order + + first_state, _ = Spree::Order.checkout_steps.first + Spree::Order.state_machine.after_transition :to => first_state do |order| + order.did_transition = true + end + end + + it "correctly calls the transition callback" do + order.did_transition.should be_nil + spree_put :update, { :checkout => "checkout" }, { :order_id => 1} + order.did_transition.should be_true + end + end + end +end diff --git a/core/spec/controllers/spree/products_controller_spec.rb b/core/spec/controllers/spree/products_controller_spec.rb index 9f72d99e125..e83052fd8b6 100644 --- a/core/spec/controllers/spree/products_controller_spec.rb +++ b/core/spec/controllers/spree/products_controller_spec.rb @@ -14,4 +14,12 @@ spree_get :show, :id => product.to_param response.status.should == 404 end + + it "should provide the current user to the searcher class" do + user = stub(:last_incomplete_spree_order => nil) + controller.stub :spree_current_user => user + Spree::Config.searcher_class.any_instance.should_receive(:current_user=).with(user) + spree_get :index + response.status.should == 200 + end end diff --git a/core/spec/controllers/spree/taxons_controller_spec.rb b/core/spec/controllers/spree/taxons_controller_spec.rb new file mode 100644 index 00000000000..9ca6cb7da74 --- /dev/null +++ b/core/spec/controllers/spree/taxons_controller_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe Spree::TaxonsController do + it "should provide the current user to the searcher class" do + taxon = create(:taxon, :permalink => "test") + user = stub(:last_incomplete_spree_order => nil) + controller.stub :spree_current_user => user + Spree::Config.searcher_class.any_instance.should_receive(:current_user=).with(user) + spree_get :show, :id => taxon.permalink + response.status.should == 200 + end +end diff --git a/core/spec/helpers/base_helper_spec.rb b/core/spec/helpers/base_helper_spec.rb index 3f1342bb475..bae571bde02 100644 --- a/core/spec/helpers/base_helper_spec.rb +++ b/core/spec/helpers/base_helper_spec.rb @@ -74,4 +74,34 @@ end end + + # Regression test for #2034 + context "flash_message" do + let(:flash) { {:notice => "ok", :foo => "foo", :bar => "bar"} } + + it "should output all flash content" do + flash_messages + html = Nokogiri::HTML(helper.output_buffer) + html.css(".notice").text.should == "ok" + html.css(".foo").text.should == "foo" + html.css(".bar").text.should == "bar" + end + + it "should output flash content except one key" do + flash_messages(:ignore_types => :bar) + html = Nokogiri::HTML(helper.output_buffer) + html.css(".notice").text.should == "ok" + html.css(".foo").text.should == "foo" + html.css(".bar").text.should be_empty + end + + it "should output flash content except some keys" do + flash_messages(:ignore_types => [:foo, :bar]) + html = Nokogiri::HTML(helper.output_buffer) + html.css(".notice").text.should == "ok" + html.css(".foo").text.should be_empty + html.css(".bar").text.should be_empty + helper.output_buffer.should == "
      ok
      " + end + end end diff --git a/core/spec/lib/money_spec.rb b/core/spec/lib/money_spec.rb new file mode 100644 index 00000000000..e60f010d13b --- /dev/null +++ b/core/spec/lib/money_spec.rb @@ -0,0 +1,65 @@ +#encoding: UTF-8 +require 'spec_helper' + +module Spree + describe Money do + before do + reset_spree_preferences do |config| + config.currency = "USD" + config.currency_symbol_position = :before + config.display_currency = false + end + end + + it "formats correctly" do + money = Spree::Money.new(10) + money.to_s.should == "$10.00" + end + + context "with currency" do + it "passed in option" do + money = Spree::Money.new(10, :with_currency => true) + money.to_s.should == "$10.00 USD" + end + + it "config option" do + Spree::Config[:display_currency] = true + money = Spree::Money.new(10) + money.to_s.should == "$10.00 USD" + end + end + + context "symbol positioning" do + it "passed in option" do + money = Spree::Money.new(10, :symbol_position => :after) + money.to_s.should == "10.00 $" + end + + it "passed in option string" do + money = Spree::Money.new(10, :symbol_position => "after") + money.to_s.should == "10.00 $" + end + + it "config option" do + Spree::Config[:currency_symbol_position] = :after + money = Spree::Money.new(10) + money.to_s.should == "10.00 $" + end + end + + context "JPY" do + before do + reset_spree_preferences do |config| + config.currency = "JPY" + config.currency_symbol_position = :before + config.display_currency = false + end + end + + it "formats correctly" do + money = Spree::Money.new(1000) + money.to_s.should == "¥1,000" + end + end + end +end diff --git a/core/spec/lib/search/base_spec.rb b/core/spec/lib/search/base_spec.rb index d8a1a8c96a6..3b2ff8df725 100644 --- a/core/spec/lib/search/base_spec.rb +++ b/core/spec/lib/search/base_spec.rb @@ -50,4 +50,11 @@ searcher.retrieve_products.count.should == 1 end + it "accepts a current user" do + user = stub + searcher = Spree::Core::Search::Base.new({}) + searcher.current_user = user + searcher.current_user.should eql(user) + end + end diff --git a/core/spec/mailers/order_mailer_spec.rb b/core/spec/mailers/order_mailer_spec.rb index 51f06734090..b9d7bb550a9 100644 --- a/core/spec/mailers/order_mailer_spec.rb +++ b/core/spec/mailers/order_mailer_spec.rb @@ -31,7 +31,7 @@ end let!(:confirmation_email) { Spree::OrderMailer.confirm_email(order) } - let!(:cancel_email) { Spree::OrderMailer.confirm_email(order) } + let!(:cancel_email) { Spree::OrderMailer.cancel_email(order) } specify do confirmation_email.body.should_not include("Ineligible Adjustment") @@ -42,4 +42,57 @@ end end + context "emails must be translatable" do + context "en locale" do + before do + en_confirm_mail = { :order_mailer => { :confirm_email => { :dear_customer => 'Dear Customer,' } } } + en_cancel_mail = { :order_mailer => { :cancel_email => { :order_summary_canceled => 'Order Summary [CANCELED]' } } } + I18n.backend.store_translations :en, en_confirm_mail + I18n.backend.store_translations :en, en_cancel_mail + I18n.locale = :en + end + + context "confirm_email" do + specify do + confirmation_email = Spree::OrderMailer.confirm_email(order) + confirmation_email.body.should include("Dear Customer,") + end + end + + context "cancel_email" do + specify do + cancel_email = Spree::OrderMailer.cancel_email(order) + cancel_email.body.should include("Order Summary [CANCELED]") + end + end + end + + context "pt-BR locale" do + before do + pt_br_confirm_mail = { :order_mailer => { :confirm_email => { :dear_customer => 'Caro Cliente,' } } } + pt_br_cancel_mail = { :order_mailer => { :cancel_email => { :order_summary_canceled => 'Resumo da Pedido [CANCELADA]' } } } + I18n.backend.store_translations :'pt-BR', pt_br_confirm_mail + I18n.backend.store_translations :'pt-BR', pt_br_cancel_mail + I18n.locale = :'pt-BR' + end + + after do + I18n.locale = I18n.default_locale + end + + context "confirm_email" do + specify do + confirmation_email = Spree::OrderMailer.confirm_email(order) + confirmation_email.body.should include("Caro Cliente,") + end + end + + context "cancel_email" do + specify do + cancel_email = Spree::OrderMailer.cancel_email(order) + cancel_email.body.should include("Resumo da Pedido [CANCELADA]") + end + end + end + end end diff --git a/core/spec/mailers/shipment_mailer_spec.rb b/core/spec/mailers/shipment_mailer_spec.rb index 6c368dd36f3..a4882e4cfbc 100644 --- a/core/spec/mailers/shipment_mailer_spec.rb +++ b/core/spec/mailers/shipment_mailer_spec.rb @@ -16,9 +16,42 @@ shipment end - it "doesn't include out of stock html span in the email body" do - Spree::Config.allow_backorders = false + # Regression test for #2196 + it "doesn't include out of stock in the email body" do shipment_email = Spree::ShipmentMailer.shipped_email(shipment) - shipment_email.body.should_not include(%Q{span class="out-of-stock"}) + shipment_email.body.should_not include(%Q{Out of Stock}) + end + + context "emails must be translatable" do + context "shipped_email" do + context "en locale" do + before do + en_shipped_email = { :shipment_mailer => { :shipped_email => { :dear_customer => 'Dear Customer,' } } } + I18n.backend.store_translations :en, en_shipped_email + I18n.locale = :en + end + + specify do + shipped_email = Spree::ShipmentMailer.shipped_email(shipment) + shipped_email.body.should include("Dear Customer,") + end + end + context "pt-BR locale" do + before do + pt_br_shipped_email = { :shipment_mailer => { :shipped_email => { :dear_customer => 'Caro Cliente,' } } } + I18n.backend.store_translations :'pt-BR', pt_br_shipped_email + I18n.locale = :'pt-BR' + end + + after do + I18n.locale = I18n.default_locale + end + + specify do + shipped_email = Spree::ShipmentMailer.shipped_email(shipment) + shipped_email.body.should include("Caro Cliente,") + end + end + end end end diff --git a/core/spec/models/adjustment_spec.rb b/core/spec/models/adjustment_spec.rb index 256488f19ff..2410635e681 100644 --- a/core/spec/models/adjustment_spec.rb +++ b/core/spec/models/adjustment_spec.rb @@ -101,5 +101,23 @@ end end + context "#display_amount" do + before { adjustment.amount = 10.55 } + context "with display_currency set to true" do + before { Spree::Config[:display_currency] = true } + + it "shows the currency" do + adjustment.display_amount.should == "$10.55 USD" + end + end + + context "with display_currency set to false" do + before { Spree::Config[:display_currency] = false } + + it "does not include the currency" do + adjustment.display_amount.should == "$10.55" + end + end + end end diff --git a/core/spec/models/calculator/price_sack_spec.rb b/core/spec/models/calculator/price_sack_spec.rb index 6911fc3c63d..f26ed8d4d58 100644 --- a/core/spec/models/calculator/price_sack_spec.rb +++ b/core/spec/models/calculator/price_sack_spec.rb @@ -1,7 +1,14 @@ require 'spec_helper' describe Spree::Calculator::PriceSack do - let(:calculator) { Spree::Calculator::PriceSack.new } + let(:calculator) do + calculator = Spree::Calculator::PriceSack.new + calculator.preferred_minimal_amount = 5 + calculator.preferred_normal_amount = 10 + calculator.preferred_discount_amount = 1 + calculator + end + let(:order) { stub_model(Spree::Order) } let(:shipment) { stub_model(Spree::Shipment) } @@ -14,4 +21,10 @@ it "computes with a snipment object" do calculator.compute(shipment) end + + # Regression test for #2055 + it "computes the correct amount" do + calculator.compute(2).should == calculator.preferred_normal_amount + calculator.compute(6).should == calculator.preferred_discount_amount + end end diff --git a/core/spec/models/credit_card_spec.rb b/core/spec/models/credit_card_spec.rb index 58081503b66..9613e6349b0 100644 --- a/core/spec/models/credit_card_spec.rb +++ b/core/spec/models/credit_card_spec.rb @@ -191,5 +191,11 @@ def stub_rails_env(environment) credit_card.spree_cc_type.should == "master" end end + + context "#associations" do + it "should be able to access its payments" do + lambda { credit_card.payments.all }.should_not raise_error ActiveRecord::StatementInvalid + end + end end diff --git a/core/spec/models/inventory_unit_spec.rb b/core/spec/models/inventory_unit_spec.rb index 30f2d532f34..8ae2a763d67 100644 --- a/core/spec/models/inventory_unit_spec.rb +++ b/core/spec/models/inventory_unit_spec.rb @@ -258,6 +258,17 @@ inventory_unit.variant.should_receive(:save) inventory_unit.return! end + + # Regression test for #2074 + context "with inventory tracking disabled" do + before { Spree::Config[:track_inventory_levels] = false } + + it "does not update on_hand for variant" do + inventory_unit.variant.should_not_receive(:on_hand=).with(96) + inventory_unit.variant.should_not_receive(:save) + inventory_unit.return! + end + end end end diff --git a/core/spec/models/line_item_spec.rb b/core/spec/models/line_item_spec.rb index 005457f46bd..68a4dc03941 100644 --- a/core/spec/models/line_item_spec.rb +++ b/core/spec/models/line_item_spec.rb @@ -22,14 +22,18 @@ end context '#save' do - it 'should update inventory and totals' do + it 'should update inventory, totals, and tax' do Spree::InventoryUnit.stub(:increase) line_item.should_receive(:update_inventory) + # Regression check for #1481 + order.should_receive(:create_tax_charge!) order.should_receive(:update!) line_item.save end context 'when order#completed? is true' do + # We don't care about this method for these tests + before { line_item.stub(:update_order) } context 'and line_item is a new record' do before { line_item.stub(:new_record? => true) } @@ -37,6 +41,8 @@ it 'should increase inventory' do Spree::InventoryUnit.stub(:increase) Spree::InventoryUnit.should_receive(:increase).with(order, variant, 5) + # We don't care about this method for this test + line_item.stub(:update_order) line_item.save end end @@ -72,7 +78,11 @@ end context 'when order#completed? is false' do - before { order.stub(:completed? => false) } + before do + order.stub(:completed? => false) + # We don't care about this method for this test + line_item.stub(:update_order) + end it 'should not manage inventory' do Spree::InventoryUnit.should_not_receive(:increase) @@ -83,8 +93,18 @@ end context '#destroy' do + # Regression test for #1481 + it "applies tax adjustments" do + # We don't care about this method for this test + line_item.stub(:remove_inventory) + order.should_receive(:create_tax_charge!) + line_item.destroy + end + context 'when order.completed? is true' do it 'should remove inventory' do + # We don't care about this method for this test + line_item.stub(:update_order) Spree::InventoryUnit.should_receive(:decrease).with(order, variant, 5) line_item.destroy end @@ -106,6 +126,8 @@ end it 'should allow destroy when no units have shipped' do + # We don't care about this method for this test + line_item.stub(:update_order) line_item.should_receive(:remove_inventory) line_item.destroy.should be_true end @@ -186,9 +208,15 @@ shipping_method = mock_model(Spree::ShippingMethod, :calculator => mock(:calculator)) shipment = Spree::Shipment.new :order => order, :shipping_method => shipping_method shipment.stub(:state => 'shipped') - inventory_units = 5.times.map { Spree::InventoryUnit.new({:variant => line_item.variant}, :without_protection => true) } + shipped_inventory_units = 5.times.map { Spree::InventoryUnit.new({ :variant => line_item.variant, :state => 'shipped' }, :without_protection => true) } + unshipped_inventory_units = 2.times.map { Spree::InventoryUnit.new({ :variant => line_item.variant, :state => 'sold' }, :without_protection => true) } + inventory_units = shipped_inventory_units + unshipped_inventory_units order.stub(:shipments => [shipment]) shipment.stub(:inventory_units => inventory_units) + inventory_units.stub(:shipped => shipped_inventory_units) + shipped_inventory_units.stub(:where).with(:variant_id => line_item.variant_id).and_return(shipped_inventory_units) + # We don't care about this method for these test + line_item.stub(:update_order) end it 'should not allow quantity to be adjusted lower than already shipped units' do diff --git a/core/spec/models/order/callbacks_spec.rb b/core/spec/models/order/callbacks_spec.rb index 2dd39c63865..f7f676a3257 100644 --- a/core/spec/models/order/callbacks_spec.rb +++ b/core/spec/models/order/callbacks_spec.rb @@ -2,6 +2,9 @@ describe Spree::Order do let(:order) { stub_model(Spree::Order) } + before do + Spree::Order.define_state_machine! + end context "validations" do context "email validation" do diff --git a/core/spec/models/order/checkout_spec.rb b/core/spec/models/order/checkout_spec.rb index d1a01298f81..43a1685fc04 100644 --- a/core/spec/models/order/checkout_spec.rb +++ b/core/spec/models/order/checkout_spec.rb @@ -2,6 +2,7 @@ describe Spree::Order do let(:order) { Spree::Order.new } + context "with default state machine" do it "has the following transitions" do transitions = [ @@ -129,13 +130,77 @@ context "without confirmation required" do before do order.stub :confirmation_required? => false + order.stub :payment_required? => true end it "transitions to complete" do + order.should_receive(:process_payments!).once order.next! order.state.should == "complete" end end + + # Regression test for #2028 + context "when payment is not required" do + before do + order.stub :payment_required? => false + end + + it "does not call process payments" do + order.should_not_receive(:process_payments!) + order.next! + order.state.should == "complete" + end + end + end + end + + context "subclassed order" do + # This causes another test above to fail, but fixing this test should make + # the other test pass + class SubclassedOrder < Spree::Order + checkout_flow do + go_to_state :payment + go_to_state :complete + end + end + + it "should only call default transitions once when checkout_flow is redefined" do + order = SubclassedOrder.new + order.stub :payment_required? => true + order.should_receive(:process_payments!).once + order.state = "payment" + order.next! + order.state.should == "complete" + end + end + + context "re-define checkout flow" do + before do + @old_checkout_flow = Spree::Order.checkout_flow + Spree::Order.class_eval do + checkout_flow do + go_to_state :payment + go_to_state :complete + end + end + end + + after do + Spree::Order.checkout_flow = @old_checkout_flow + end + + it "should not keep old event transitions when checkout_flow is redefined" do + Spree::Order.next_event_transitions.should == [{:cart=>:payment}, {:payment=>:complete}] + end + + it "should not keep old events when checkout_flow is redefined" do + state_machine = Spree::Order.state_machine + state_machine.states.any? { |s| s.name == :address }.should be_false + known_states = state_machine.events[:next].branches.map(&:known_states).flatten + known_states.should_not include(:address) + known_states.should_not include(:delivery) + known_states.should_not include(:confirm) end end end diff --git a/core/spec/models/order/payment_spec.rb b/core/spec/models/order/payment_spec.rb new file mode 100644 index 00000000000..5985d6d16bc --- /dev/null +++ b/core/spec/models/order/payment_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +module Spree + describe Order do + let(:order) { stub_model(Order) } + + before do + # So that Payment#purchase! is called during processing + Spree::Config[:auto_capture] = true + + order.stub_chain(:line_items, :empty?).and_return(false) + order.stub :total => 100 + end + + it 'processes all payments' do + payment_1 = create(:payment, :amount => 50) + payment_2 = create(:payment, :amount => 50) + order.stub(:pending_payments).and_return([payment_1, payment_2]) + + order.process_payments! + order.send(:update_payment_state) + order.payment_state.should == 'paid' + + payment_1.should be_completed + payment_2.should be_completed + end + + it 'does not go over total for order' do + payment_1 = create(:payment, :amount => 50) + payment_2 = create(:payment, :amount => 50) + payment_3 = create(:payment, :amount => 50) + order.stub(:pending_payments).and_return([payment_1, payment_2, payment_3]) + + order.process_payments! + order.send(:update_payment_state) + order.payment_state.should == 'paid' + + payment_1.should be_completed + payment_2.should be_completed + payment_3.should be_pending + end + + it "does not use failed payments" do + payment_1 = create(:payment, :amount => 50) + payment_2 = create(:payment, :amount => 50, :state => 'failed') + order.stub(:pending_payments).and_return([payment_1]) + + payment_2.should_not_receive(:process!) + + order.process_payments! + end + end +end diff --git a/core/spec/models/order/state_machine_spec.rb b/core/spec/models/order/state_machine_spec.rb index 3d3f13bbcb6..45ed7b79398 100644 --- a/core/spec/models/order/state_machine_spec.rb +++ b/core/spec/models/order/state_machine_spec.rb @@ -3,6 +3,8 @@ describe Spree::Order do let(:order) { Spree::Order.new } before do + # Ensure state machine has been re-defined correctly + Spree::Order.define_state_machine! # We don't care about this validation here order.stub(:require_email) end @@ -19,6 +21,7 @@ context "when credit card payment fails" do before do order.stub(:process_payments!).and_raise(Spree::Core::GatewayError) + order.stub :payment_required? => true end context "when not configured to allow failed payments" do @@ -119,6 +122,8 @@ end it "should send a cancel email" do + # Stub methods that cause side-effects in this test + order.stub :has_available_shipment order.stub :restock_items! mail_message = mock "Mail::Message" Spree::OrderMailer.should_receive(:cancel_email).with(order).and_return mail_message @@ -132,6 +137,8 @@ shipment.stub(:update_order) Spree::OrderMailer.stub(:cancel_email).and_return(mail_message = stub) mail_message.stub :deliver + + order.stub :has_available_shipment end # Regression fix for #729 @@ -144,8 +151,11 @@ context "resets payment state" do before do # TODO: This is ugly :( + # Stubs methods that cause unwanted side effects in this test Spree::OrderMailer.stub(:cancel_email).and_return(mail_message = stub) mail_message.stub :deliver + order.stub :has_available_shipment + order.stub :restock_items! end context "without shipped items" do @@ -170,19 +180,15 @@ it "should change shipment status (unless shipped)" end - # Another regression test for #729 context "#resume" do before do order.stub :email => "user@spreecommerce.com" order.stub :state => "canceled" order.stub :allow_resume? => true - end - it "should send a resume email" do - pending "Pending test for #818" - order.stub :unstock_items! - order.resume! + # Stubs method that cause unwanted side effects in this test + order.stub :has_available_shipment end context "unstocks inventory" do diff --git a/core/spec/models/order_spec.rb b/core/spec/models/order_spec.rb index 25b1721b92f..76916c6dd8c 100644 --- a/core/spec/models/order_spec.rb +++ b/core/spec/models/order_spec.rb @@ -85,7 +85,15 @@ def compute(computable) Spree::InventoryUnit.should_receive(:assign_opening_inventory).with(order) order.finalize! end - it "should change the shipment state to ready if order is paid" + it "should change the shipment state to ready if order is paid" do + order.stub :shipping_method => mock_model(Spree::ShippingMethod, :create_adjustment => true) + order.create_shipment! + order.stub(:paid? => true, :complete? => true) + order.finalize! + order.reload # reload so we're sure the changes are persisted + order.shipment.state.should == 'ready' + order.shipment_state.should == 'ready' + end after { Spree::Config.set :track_inventory_levels => true } it "should not sell inventory units if track_inventory_levels is false" do @@ -106,11 +114,17 @@ def compute(computable) order.finalize! end - it "should freeze optional adjustments" do + it "should freeze all adjustments" do + # Stub this method as it's called due to a callback + # and it's irrelevant to this test + order.stub :has_available_shipment + Spree::OrderMailer.stub_chain :confirm_email, :deliver - adjustment = mock_model(Spree::Adjustment) - order.stub_chain :adjustments, :optional => [adjustment] - adjustment.should_receive(:update_column).with("locked", true) + adjustment1 = mock_model(Spree::Adjustment, :mandatory => true) + adjustment2 = mock_model(Spree::Adjustment, :mandatory => false) + order.stub :adjustments => [adjustment1, adjustment2] + adjustment1.should_receive(:update_column).with("locked", true) + adjustment2.should_receive(:update_column).with("locked", true) order.finalize! end @@ -122,8 +136,11 @@ def compute(computable) context "#process_payments!" do it "should process the payments" do - order.stub!(:payments).and_return([mock(Spree::Payment)]) - order.payment.should_receive(:process!) + order.stub(:total).and_return(10) + payment = stub_model(Spree::Payment) + payments = [payment] + order.stub(:payments).and_return(payments) + payments.first.should_receive(:process!) order.process_payments! end end @@ -175,16 +192,16 @@ def compute(computable) context "#backordered?" do it "should indicate whether any units in the order are backordered" do - order.stub_chain(:inventory_units, :backorder).and_return [] + order.stub_chain(:inventory_units, :backordered).and_return [] order.backordered?.should be_false - order.stub_chain(:inventory_units, :backorder).and_return [mock_model(Spree::InventoryUnit)] + order.stub_chain(:inventory_units, :backordered).and_return [mock_model(Spree::InventoryUnit)] order.backordered?.should be_true end it "should always be false when inventory tracking is disabled" do pending Spree::Config.set :track_inventory_levels => false - order.stub_chain(:inventory_units, :backorder).and_return [mock_model(Spree::InventoryUnit)] + order.stub_chain(:inventory_units, :backordered).and_return [mock_model(Spree::InventoryUnit)] order.backordered?.should be_false end end @@ -260,6 +277,13 @@ def compute(computable) order.completed_at = Time.now order.can_cancel?.should be_false end + + it "should be true for completed order with no shipment" do + order.state = 'complete' + order.shipment_state = nil + order.completed_at = Time.now + order.can_cancel?.should be_true + end end context "rate_hash" do @@ -554,4 +578,42 @@ def compute(computable) order.empty! end end + + context "#display_total" do + before { order.total = 10.55 } + + context "with display_currency set to true" do + before { Spree::Config[:display_currency] = true } + + it "shows the currency" do + order.display_total.to_s.should == "$10.55 USD" + end + end + + context "with display_currency set to false" do + before { Spree::Config[:display_currency] = false } + + it "does not include the currency" do + order.display_total.to_s.should == "$10.55" + end + end + end + + # Regression test for #2191 + context "when an order has an adjustment that zeroes the total, but another adjustment for shipping that raises it above zero" do + let!(:persisted_order) { create(:order) } + let!(:line_item) { create(:line_item) } + let!(:shipping_method) { create(:shipping_method) } + + before do + persisted_order.line_items << line_item + persisted_order.adjustments.create(:amount => 19.99, :label => "Promotion") + persisted_order.state = 'delivery' + end + + it "transitions from delivery to payment" do + persisted_order.shipping_method = shipping_method + persisted_order.next_transition.to.should == "payment" + end + end end diff --git a/core/spec/models/payment_spec.rb b/core/spec/models/payment_spec.rb index 738d54ed307..29050940897 100644 --- a/core/spec/models/payment_spec.rb +++ b/core/spec/models/payment_spec.rb @@ -182,11 +182,6 @@ payment.state = 'pending' end - it "should not do anything" do - payment.payment_method.should_not_receive(:capture) - payment.log_entries.should_not_receive(:create) - end - context "if sucessful" do before do payment.payment_method.should_receive(:capture).with(payment, card, anything).and_return(success_response) @@ -213,6 +208,20 @@ end end end + + # Regression test for #2119 + context "when payment is completed" do + before do + payment.state = 'completed' + end + + it "should do nothing" do + payment.should_not_receive(:complete) + payment.payment_method.should_not_receive(:capture) + payment.log_entries.should_not_receive(:create) + payment.capture! + end + end end context "#void" do @@ -265,6 +274,18 @@ lambda { payment.void_transaction! }.should raise_error(Spree::Core::GatewayError) end end + + # Regression test for #2119 + context "if payment is already voided" do + before do + payment.state = 'void' + end + + it "should not void the payment" do + payment.payment_method.should_not_receive(:void) + payment.void_transaction! + end + end end context "#credit" do diff --git a/core/spec/models/preferences/preferable_spec.rb b/core/spec/models/preferences/preferable_spec.rb index a3c5f95b08a..13b57e6af89 100644 --- a/core/spec/models/preferences/preferable_spec.rb +++ b/core/spec/models/preferences/preferable_spec.rb @@ -79,7 +79,6 @@ class B < A describe "preference access" do it "handles ghost methods for preferences" do - #pending("TODO: cmar to look at this test to figure out why it's failing on 1.9") @a.preferred_color = 'blue' @a.preferred_color.should eq 'blue' @@ -113,6 +112,24 @@ class B < A @b.preferences[:color].should eq 'green' #default from A end + context "database fallback" do + before do + @a.instance_variable_set("@pending_preferences", {}) + end + + it "retrieves a preference from the database before falling back to default" do + preference = mock(:value => "chatreuse") + Spree::Preference.should_receive(:find_by_name).with(:color).and_return(preference) + @a.preferred_color.should == 'chatreuse' + end + + it "defaults if no database key exists" do + Spree::Preference.should_receive(:find_by_name).and_return(nil) + @a.preferred_color.should == 'green' + end + end + + context "converts integer preferences to integer values" do before do A.preference :is_integer, :integer @@ -244,10 +261,10 @@ class PrefTest < ActiveRecord::Base it "saves preferences for serialized object" do pr = PrefTest.new - pr[:pref_test_any] = [1, 2] - pr[:pref_test_any].should == [1, 2] + pr.set_preference(:pref_test_any, [1, 2]) + pr.get_preference(:pref_test_any).should == [1, 2] pr.save! - pr[:pref_test_any].should == [1, 2] + pr.get_preference(:pref_test_any).should == [1, 2] end end diff --git a/core/spec/models/preferences/store_spec.rb b/core/spec/models/preferences/store_spec.rb index f63cf748c7c..2d5a091934e 100644 --- a/core/spec/models/preferences/store_spec.rb +++ b/core/spec/models/preferences/store_spec.rb @@ -15,4 +15,10 @@ @store.set :test, false, :boolean @store.get(:test).should be_false end -end \ No newline at end of file + + it "returns the correct preference value when the cache is empty" do + @store.set :test, "1", :string + Rails.cache.clear + @store.get(:test).should == "1" + end +end diff --git a/core/spec/models/product/scopes_spec.rb b/core/spec/models/product/scopes_spec.rb new file mode 100644 index 00000000000..8c3a3003309 --- /dev/null +++ b/core/spec/models/product/scopes_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe "Product scopes" do + let!(:product) { create(:product) } + + context "A product assigned to parent and child taxons" do + before do + @taxonomy = create(:taxonomy) + @root_taxon = @taxonomy.root + + @parent_taxon = create(:taxon, :name => 'Parent', :taxonomy_id => @taxonomy.id, :parent => @root_taxon) + @child_taxon = create(:taxon, :name =>'Child 1', :taxonomy_id => @taxonomy.id, :parent => @parent_taxon) + @parent_taxon.reload # Need to reload for descendents to show up + + product.taxons << @parent_taxon + product.taxons << @child_taxon + end + + it "calling Product.in_taxon should not return duplicate records" do + Spree::Product.in_taxon(@parent_taxon).to_a.count.should == 1 + end + end + + context "on_hand" do + # Regression test for #2111 + context "A product with a deleted variant" do + before do + variant = product.variants.create({:count_on_hand => 300}, :without_protection => true) + variant.update_column(:deleted_at, Time.now) + end + + it "does not include the deleted variant in on_hand summary" do + Spree::Product.on_hand.should be_empty + end + end + end +end diff --git a/core/spec/models/product_spec.rb b/core/spec/models/product_spec.rb index 75f28f483a8..f990759f5c6 100644 --- a/core/spec/models/product_spec.rb +++ b/core/spec/models/product_spec.rb @@ -36,6 +36,31 @@ end end + context "product has no variants" do + context "#delete" do + it "should set deleted_at value" do + product.delete + product.reload + product.deleted_at.should_not be_nil + product.master.deleted_at.should_not be_nil + end + end + end + + context "product has variants" do + before do + create(:variant, :product => product) + end + + context "#delete" do + it "should set deleted_at value" do + product.delete + product.deleted_at.should_not be_nil + product.variants_including_master.all? { |v| !v.deleted_at.nil? }.should be_true + end + end + end + context "#on_hand" do # Regression test for #898 context 'returns the correct number of products on hand' do @@ -47,6 +72,32 @@ end end + # Test for #2167 + context "#on_display?" do + it "is on display if product has stock" do + product.stub :has_stock? => true + assert product.on_display? + end + + it "is on display if show_zero_stock_products preference is set to true" do + Spree::Config[:show_zero_stock_products] = true + assert product.on_display? + end + end + + # Test for #2167 + context "#on_sale?" do + it "is on sale if the product has stock" do + product.stub :has_stock? => true + assert product.on_sale? + end + + it "is on sale if allow_backorders preference is set to true" do + Spree::Config[:allow_backorders] = true + assert product.on_sale? + end + end + context "#price" do # Regression test for #1173 it 'strips non-price characters' do @@ -54,6 +105,41 @@ product.price.should == 10.0 end end + + context "#display_price" do + before { product.price = 10.55 } + + context "with display_currency set to true" do + before { Spree::Config[:display_currency] = true } + + it "shows the currency" do + product.display_price.should == "$10.55 USD" + end + end + + context "with display_currency set to false" do + before { Spree::Config[:display_currency] = false } + + it "does not include the currency" do + product.display_price.should == "$10.55" + end + end + end + + context "#available?" do + it "should be available if date is in the past" do + product.available_on = 1.day.ago + product.should be_available + end + + it "should not be available if date is nil or in the future" do + product.available_on = nil + product.should_not be_available + + product.available_on = 1.day.from_now + product.should_not be_available + end + end end context "validations" do @@ -154,6 +240,29 @@ end end + context "manual permalink override" do + it "calling save_permalink with a parameter" do + @product = create(:product, :name => "foo") + @product.permalink.should == "foo" + @product.name = "foobar" + @product.save + @product.permalink.should == "foo" + @product.save_permalink(@product.name) + @product.permalink.should == "foobar" + end + + it "should be incremented until not taken with a parameter" do + @product = create(:product, :name => "foo") + @product2 = create(:product, :name => "foobar") + @product.permalink.should == "foo" + @product.name = "foobar" + @product.save + @product.permalink.should == "foo" + @product.save_permalink(@product.name) + @product.permalink.should == "foobar-1" + end + end + context "properties" do it "should properly assign properties" do product = FactoryGirl.create :product @@ -210,7 +319,7 @@ @product.option_type_ids.should == prototype.option_type_ids end - it "should create product option types based on the prototype" do + it "should create product option types based on the prototype" do @product.save @product.product_option_types.map(&:option_type_id).should == prototype.option_type_ids end diff --git a/core/spec/models/shipment_spec.rb b/core/spec/models/shipment_spec.rb index 2644660f7d4..0d58189e430 100644 --- a/core/spec/models/shipment_spec.rb +++ b/core/spec/models/shipment_spec.rb @@ -5,7 +5,7 @@ reset_spree_preferences end - let(:order) { mock_model Spree::Order, :backordered? => false } + let(:order) { mock_model Spree::Order, :backordered? => false, :complete? => true } let(:shipping_method) { mock_model Spree::ShippingMethod, :calculator => mock('calculator') } let(:shipment) do shipment = Spree::Shipment.new :order => order, :shipping_method => shipping_method @@ -44,6 +44,14 @@ end end + context "when order is incomplete" do + before { order.stub :complete? => false } + it "should result in a 'pending' state" do + shipment.should_receive(:update_column).with("state", "pending") + shipment.update!(order) + end + end + context "when order is paid" do before { order.stub :paid? => true } it "should result in a 'ready' state" do diff --git a/core/spec/models/shipping_method_spec.rb b/core/spec/models/shipping_method_spec.rb index c76405a8790..df73f0666b9 100644 --- a/core/spec/models/shipping_method_spec.rb +++ b/core/spec/models/shipping_method_spec.rb @@ -31,6 +31,34 @@ @shipping_method.available?(@order).should be_true end end + + context "whe the calculator is front_end" do + before { @shipping_method.display_on = 'front_end' } + + context "and the order is processed through the front_end" do + it "should be true" do + @shipping_method.available?(@order, :front_end).should be_true + end + + it "should be false" do + @shipping_method.available?(@order, :back_end).should be_false + end + end + end + + context "whe the calculator is back_end" do + before { @shipping_method.display_on = 'back_end' } + + context "and the order is processed through the back_end" do + it "should be false" do + @shipping_method.available?(@order, :front_end).should be_false + end + + it "should be true" do + @shipping_method.available?(@order, :back_end).should be_true + end + end + end end context 'available_to_order?' do diff --git a/core/spec/models/shipping_rate_spec.rb b/core/spec/models/shipping_rate_spec.rb new file mode 100644 index 00000000000..f8028533247 --- /dev/null +++ b/core/spec/models/shipping_rate_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe Spree::ShippingRate do + let(:shipping_rate) { Spree::ShippingRate.new(:cost => 10.55) } + before { Spree::TaxRate.stub(:default => 0.05) } + + context "#display_price" do + context "when shipment includes VAT" do + before { Spree::Config[:shipment_inc_vat] = true } + it "displays the correct price" do + shipping_rate.display_price.to_s.should == "$11.08" # $10.55 * 1.05 == $11.08 + end + end + + context "when shipment does not include VAT" do + before { Spree::Config[:shipment_inc_vat] = false } + it "displays the correct price" do + shipping_rate.display_price.to_s.should == "$10.55" + end + end + end +end diff --git a/core/spec/models/tracker_spec.rb b/core/spec/models/tracker_spec.rb index 176b792e644..b4b97ad1980 100644 --- a/core/spec/models/tracker_spec.rb +++ b/core/spec/models/tracker_spec.rb @@ -2,7 +2,7 @@ describe Spree::Tracker do describe "current" do - before(:each) { @tracker = Factory(:tracker) } + before(:each) { @tracker = create(:tracker) } it "returns the first active tracker for the environment" do Spree::Tracker.current.should == @tracker diff --git a/core/spec/models/variant_spec.rb b/core/spec/models/variant_spec.rb index d13e6bb514d..2c3a6fec013 100644 --- a/core/spec/models/variant_spec.rb +++ b/core/spec/models/variant_spec.rb @@ -28,6 +28,22 @@ variant.run_callbacks(:save) end + it "lock_version should prevent stale updates" do + copy = Spree::Variant.find(variant.id) + + copy.count_on_hand = 200 + copy.save! + + variant.count_on_hand = 100 + expect { variant.save }.to raise_error ActiveRecord::StaleObjectError + + variant.reload.count_on_hand.should == 200 + variant.count_on_hand = 100 + variant.save + + variant.reload.count_on_hand.should == 100 + end + context "on_hand=" do before { variant.stub(:inventory_units => mock('inventory-units')) } @@ -82,7 +98,7 @@ end - context "and count is decreased" do + context "and count is negative" do before { variant.inventory_units.stub(:with_state).and_return([]) } it "should change count_on_hand to given value" do @@ -192,4 +208,65 @@ end end + + context "price parsing" do + before(:each) do + I18n.locale = I18n.default_locale + I18n.backend.store_translations(:de, { :number => { :currency => { :format => { :delimiter => '.', :separator => ',' } } } }) + end + + after do + I18n.locale = I18n.default_locale + end + + context "price=" do + context "with decimal point" do + it "captures the proper amount for a formatted price" do + variant.price = '1,599.99' + variant.price.should == 1599.99 + end + end + + context "with decimal comma" do + it "captures the proper amount for a formatted price" do + I18n.locale = :de + variant.price = '1.599,99' + variant.price.should == 1599.99 + end + end + + context "with a numeric price" do + it "uses the price as is" do + I18n.locale = :de + variant.price = 1599.99 + variant.price.should == 1599.99 + end + end + end + + context "cost_price=" do + context "with decimal point" do + it "captures the proper amount for a formatted price" do + variant.cost_price = '1,599.99' + variant.cost_price.should == 1599.99 + end + end + + context "with decimal comma" do + it "captures the proper amount for a formatted price" do + I18n.locale = :de + variant.cost_price = '1.599,99' + variant.cost_price.should == 1599.99 + end + end + + context "with a numeric price" do + it "uses the price as is" do + I18n.locale = :de + variant.cost_price = 1599.99 + variant.cost_price.should == 1599.99 + end + end + end + end end diff --git a/core/spec/requests/admin/configuration/mail_methods_spec.rb b/core/spec/requests/admin/configuration/mail_methods_spec.rb index 9529d210b46..b6183d1d94e 100644 --- a/core/spec/requests/admin/configuration/mail_methods_spec.rb +++ b/core/spec/requests/admin/configuration/mail_methods_spec.rb @@ -31,8 +31,9 @@ end context "edit" do - before(:each) do - create(:mail_method) + let!(:mail_method) { create(:mail_method, :preferred_smtp_password => "haxme") } + + before do click_link "Mail Methods" end @@ -44,5 +45,16 @@ within(:css, "table.index tbody tr") { click_link "Edit" } find_field("mail_method_preferred_mail_bcc").value.should == "spree@example.com99" end + + # Regression test for #2094 + it "does not clear password if not provided" do + mail_method.preferred_smtp_password.should == "haxme" + within(:css, "table.index tbody tr") { click_link "Edit" } + click_button "Update" + page.should have_content("successfully updated!") + + mail_method.reload + mail_method.preferred_smtp_password.should_not be_blank + end end end diff --git a/core/spec/requests/admin/configuration/shipping_methods_spec.rb b/core/spec/requests/admin/configuration/shipping_methods_spec.rb index be1a32085e8..62f0b8f9027 100644 --- a/core/spec/requests/admin/configuration/shipping_methods_spec.rb +++ b/core/spec/requests/admin/configuration/shipping_methods_spec.rb @@ -81,7 +81,7 @@ visit spree.root_path click_link "Mug" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" str_addr = "bill_address" select "United States", :from => "order_#{str_addr}_attributes_country_id" @@ -108,7 +108,7 @@ visit spree.root_path click_link "Mug" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" str_addr = "bill_address" select "United States", :from => "order_#{str_addr}_attributes_country_id" @@ -137,7 +137,7 @@ visit spree.root_path click_link "Mug" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" str_addr = "bill_address" select "United States", :from => "order_#{str_addr}_attributes_country_id" @@ -162,7 +162,7 @@ visit spree.root_path click_link "Mug" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" str_addr = "bill_address" select "United States", :from => "order_#{str_addr}_attributes_country_id" @@ -198,7 +198,7 @@ click_link "Home" click_link "Shirt" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" str_addr = "bill_address" select "United States", :from => "order_#{str_addr}_attributes_country_id" @@ -226,7 +226,7 @@ click_link "Home" click_link "Shirt" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" str_addr = "bill_address" select "United States", :from => "order_#{str_addr}_attributes_country_id" diff --git a/core/spec/requests/admin/configuration/states_spec.rb b/core/spec/requests/admin/configuration/states_spec.rb index 56d4d0cd483..63938f3e8b9 100644 --- a/core/spec/requests/admin/configuration/states_spec.rb +++ b/core/spec/requests/admin/configuration/states_spec.rb @@ -37,7 +37,12 @@ it "should show validation errors", :js => true do click_link "States" select country.name, :from => "country" + + wait_until do + page.should have_selector("#new_state_link", :visible => true) + end click_link "new_state_link" + fill_in "state_name", :with => "" fill_in "Abbreviation", :with => "" click_button "Create" diff --git a/core/spec/requests/admin/configuration/tax_rates_spec.rb b/core/spec/requests/admin/configuration/tax_rates_spec.rb index d8fe3c0a83b..ee60ce405bd 100644 --- a/core/spec/requests/admin/configuration/tax_rates_spec.rb +++ b/core/spec/requests/admin/configuration/tax_rates_spec.rb @@ -14,7 +14,7 @@ it "can see a tax rate in the list if the tax category has been deleted" do tax_rate.tax_category.mark_deleted! lambda { click_link "Tax Rates" }.should_not raise_error - within("table tbody td:nth-child(2)") do + within("table tbody td:nth-child(3)") do page.should have_content("N/A") end end diff --git a/core/spec/requests/admin/orders/customer_details_spec.rb b/core/spec/requests/admin/orders/customer_details_spec.rb index f86c30916e7..13cc49dee22 100644 --- a/core/spec/requests/admin/orders/customer_details_spec.rb +++ b/core/spec/requests/admin/orders/customer_details_spec.rb @@ -5,16 +5,27 @@ let(:shipping_method) { create(:shipping_method, :display_on => "front_end") } let(:order) { create(:order_with_inventory_unit_shipped, :completed_at => 1.year.ago, :shipping_method => shipping_method) } + let(:country) do + create(:country, :name => "Kangaland") + end + + let(:state) do + create(:state, :name => "Alabama", :country => country) + end before do reset_spree_preferences do |config| - config.default_country_id = create(:country).id + config.default_country_id = country.id + config.company = true end create(:shipping_method, :display_on => "front_end") create(:order_with_inventory_unit_shipped, :completed_at => "2011-02-01 12:36:15") - create(:user, :email => 'foobar@example.com', :ship_address => create(:address), :bill_address => create(:address)) - create(:country) + ship_address = create(:address, :country => country, :state => state) + bill_address = create(:address, :country => country, :state => state) + create(:user, :email => 'foobar@example.com', + :ship_address => ship_address, + :bill_address => bill_address) visit spree.admin_path click_link "Orders" @@ -23,11 +34,9 @@ context "editing an order", :js => true do it "should be able to populate customer details for an existing order" do - pending "Waiting on Jones Lee to fix customer searching" click_link "Customer Details" fill_in "customer_search", :with => "foobar" sleep(3) - page.execute_script %Q{ $('.ui-menu-item a:contains("foobar@example.com")').trigger("mouseenter").click(); } ["ship_address", "bill_address"].each do |address| @@ -38,32 +47,30 @@ find_field("order_#{address}_attributes_address2").value.should == "Northwest" find_field("order_#{address}_attributes_city").value.should == "Herndon" find_field("order_#{address}_attributes_zipcode").value.should == "20170" - find_field("order_#{address}_attributes_state_id").find('option[selected]').text.should == "Alabama" - find_field("order_#{address}_attributes_country_id").find('option[selected]').text.should == "United States" + find_field("order_#{address}_attributes_state_id").value.should == state.id.to_s + find_field("order_#{address}_attributes_country_id").value.should == country.id.to_s find_field("order_#{address}_attributes_phone").value.should == "123-456-7890" end end it "should be able to update customer details for an existing order" do - pending "Hanging at line 60" order.ship_address = create(:address) order.save! click_link "Customer Details" - fill_in "order_ship_address_attributes_firstname", :with => "John 99" - fill_in "order_ship_address_attributes_lastname", :with => "Doe" - fill_in "order_ship_address_attributes_lastname", :with => "Company" - fill_in "order_ship_address_attributes_address1", :with => "100 first lane" - fill_in "order_ship_address_attributes_address2", :with => "#101" - fill_in "order_ship_address_attributes_city", :with => "Bethesda" - fill_in "order_ship_address_attributes_zipcode", :with => "20170" - fill_in "order_ship_address_attributes_state_name", :with => "Alabama" - fill_in "order_ship_address_attributes_phone", :with => "123-456-7890" - click_button "Continue" + ["ship", "bill"].each do |type| + fill_in "order_#{type}_address_attributes_firstname", :with => "John 99" + fill_in "order_#{type}_address_attributes_lastname", :with => "Doe" + fill_in "order_#{type}_address_attributes_lastname", :with => "Company" + fill_in "order_#{type}_address_attributes_address1", :with => "100 first lane" + fill_in "order_#{type}_address_attributes_address2", :with => "#101" + fill_in "order_#{type}_address_attributes_city", :with => "Bethesda" + fill_in "order_#{type}_address_attributes_zipcode", :with => "20170" + select "Alabama", :from => "order_#{type}_address_attributes_state_id" + fill_in "order_#{type}_address_attributes_phone", :with => "123-456-7890" + end - visit spree.admin_path - click_link "Orders" - within(:css, 'table#listing_orders') { click_link "Edit" } + click_button "Continue" click_link "Customer Details" find_field('order_ship_address_attributes_firstname').value.should == "John 99" diff --git a/core/spec/requests/admin/orders/order_details_spec.rb b/core/spec/requests/admin/orders/order_details_spec.rb index 9db1ae5b83a..3387f8d5dfa 100644 --- a/core/spec/requests/admin/orders/order_details_spec.rb +++ b/core/spec/requests/admin/orders/order_details_spec.rb @@ -51,17 +51,14 @@ I18n.backend.store_translations I18n.locale, :shipment_state => { :missing => 'some text' }, - :payment_states => { :missing => 'other text' }, - :number => { :currency => { :format => { - :format => "%n—%u", - :unit => "£" - }}} + :payment_states => { :missing => 'other text' } + + Spree::Config[:currency] = "GBP" visit spree.edit_admin_order_path(order) within "#sidebar" do - # beware - the dash before pound is really em dash character '—' - find("#order_total").text.should == "#{I18n.t(:total)}: 0.00—£" + find("#order_total").text.should == "#{I18n.t(:total)}: £0.00" find("#shipment_status").text.should == "#{I18n.t(:shipment)}: some text" find("#payment_status").text.should == "#{I18n.t(:payment)}: other text" end diff --git a/core/spec/requests/admin/products/edit/taxons_spec.rb b/core/spec/requests/admin/products/edit/taxons_spec.rb index 902c8727242..34f912ffe37 100644 --- a/core/spec/requests/admin/products/edit/taxons_spec.rb +++ b/core/spec/requests/admin/products/edit/taxons_spec.rb @@ -5,7 +5,7 @@ context "managing taxons" do def selected_taxons - find("#product_taxon_ids").value.map(&:to_i) + find("#product_taxon_ids").value.split(',').map(&:to_i) end it "should allow an admin to manage taxons", :js => true do @@ -20,10 +20,14 @@ def selected_taxons click_link "Edit" end + find(".select2-search-choice").text.should == taxon_1.name selected_taxons.should =~ [taxon_1.id] select2("#product_taxons_field", "Clothing") click_button "Update" selected_taxons.should =~ [taxon_1.id, taxon_2.id] + + # Regression test for #2139 + all("#s2id_product_taxon_ids .select2-search-choice").count.should == 2 end end end diff --git a/core/spec/requests/admin/products/edit/variants_spec.rb b/core/spec/requests/admin/products/edit/variants_spec.rb index 8793f8a35bc..ef31db32db8 100644 --- a/core/spec/requests/admin/products/edit/variants_spec.rb +++ b/core/spec/requests/admin/products/edit/variants_spec.rb @@ -37,7 +37,7 @@ visit spree.admin_path click_link "Products" within('table.index tr:nth-child(2)') { click_link "Edit" } - select2('#product_option_types_field', 'color') + select "colors", :from => "Option Types" click_button "Update" page.should have_content("successfully updated!") diff --git a/core/spec/requests/admin/products/products_spec.rb b/core/spec/requests/admin/products/products_spec.rb index 17913f6abc9..ef367ffce49 100644 --- a/core/spec/requests/admin/products/products_spec.rb +++ b/core/spec/requests/admin/products/products_spec.rb @@ -143,6 +143,18 @@ page.should have_content("Name can't be blank") page.should have_content("Price can't be blank") end + + # Regression test for #2097 + it "can set the count on hand to a null value", :js => true do + fill_in "product_name", :with => "Baseball Cap" + fill_in "product_price", :with => "100" + click_button "Create" + page.should have_content("successfully created!") + fill_in "product_on_hand", :with => "" + click_button "Update" + page.should_not have_content("spree_products.count_on_hand may not be NULL") + page.should have_content("successfully updated!") + end end context "cloning a product", :js => true do diff --git a/core/spec/requests/admin/products/prototypes_spec.rb b/core/spec/requests/admin/products/prototypes_spec.rb index a977784c50f..da513125a0e 100644 --- a/core/spec/requests/admin/products/prototypes_spec.rb +++ b/core/spec/requests/admin/products/prototypes_spec.rb @@ -59,4 +59,35 @@ page.should have_content("Shirt 99") end end + + context "editing a prototype" do + it "should allow to empty its properties" do + create(:property, :name => "model", :presentation => "Model") + create(:property, :name => "brand", :presentation => "Brand") + + shirt_prototype = create(:prototype, :name => "Shirt", :properties => []) + %w( brand model ).each do |prop| + shirt_prototype.properties << Spree::Property.find_by_name(prop) + end + + visit spree.admin_path + click_link "Products" + click_link "Prototypes" + + click_on "Edit" + + page.should have_checked_field('brand') + page.should have_checked_field('model') + + page.uncheck('brand') + page.uncheck('model') + + click_button 'Update' + + click_on "Edit" + + page.should have_unchecked_field('brand') + page.should have_unchecked_field('model') + end + end end diff --git a/core/spec/requests/cart_spec.rb b/core/spec/requests/cart_spec.rb index a978180d356..81be9c406f2 100644 --- a/core/spec/requests/cart_spec.rb +++ b/core/spec/requests/cart_spec.rb @@ -10,4 +10,38 @@ visit spree.cart_path lambda { find("li#link-to-cart a") }.should raise_error(Capybara::ElementNotFound) end + + it "prevents double clicking the remove button on cart", :js => true do + @product = create(:product, :name => "RoR Mug", :on_hand => 1) + + visit spree.root_path + click_link "RoR Mug" + click_button "add-to-cart-button" + + # prevent form submit to verify button is disabled + page.execute_script("$('#update-cart').submit(function(){return false;})") + + page.should_not have_selector('button#update-button[disabled]') + page.find(:css, '.delete img').click + page.should have_selector('button#update-button[disabled]') + end + + # Regression test for #2006 + it "does not error out with a 404 when GET'ing to /orders/populate" do + visit '/orders/populate' + within(".error") do + page.should have_content(I18n.t(:populate_get_error)) + end + end + + it 'allows you to remove an item from the cart', :js => true do + create(:product, :name => "RoR Mug", :on_hand => 1) + visit spree.root_path + click_link "RoR Mug" + click_button "add-to-cart-button" + within("#line_items") do + click_link "delete_line_item_1" + end + page.should_not have_content("Line items quantity must be an integer") + end end diff --git a/core/spec/requests/checkout_spec.rb b/core/spec/requests/checkout_spec.rb index 2213d3c3fc4..4062c856c24 100644 --- a/core/spec/requests/checkout_spec.rb +++ b/core/spec/requests/checkout_spec.rb @@ -3,20 +3,23 @@ describe "Checkout" do let(:country) { create(:country, :name => "Kangaland") } before do - Factory(:state, :name => "Victoria", :country => country) + create(:state, :name => "Victoria", :country => country) end context "visitor makes checkout as guest without registration" do + before(:each) do + Spree::Product.delete_all + @product = create(:product, :name => "RoR Mug") + @product.on_hand = 1 + @product.save + create(:zone) + end + context "when backordering is disabled" do before(:each) do reset_spree_preferences do |config| config.allow_backorders = false end - Spree::Product.delete_all - @product = create(:product, :name => "RoR Mug") - @product.on_hand = 1 - @product.save - create(:zone) end it "should warn the user about out of stock items" do @@ -28,17 +31,54 @@ @product.on_hand = 0 @product.save - click_link "Checkout" + click_button "Checkout" within(:css, "span.out-of-stock") { page.should have_content("Out of Stock") } end + end + + context "and likes to double click buttons" do + before(:each) do + @order = create(:order_with_totals, :state => 'payment', + :bill_address => create(:address), + :ship_address => create(:address), + :shipping_method => create(:shipping_method)) + @order.reload + @order.update! + + @order.stub(:available_payment_methods => [ create(:bogus_payment_method, :environment => 'test') ]) + Spree::CheckoutController.any_instance.stub(:current_order => @order) + Spree::CheckoutController.any_instance.stub(:skip_state_validation? => true) + end + + it "prevents double clicking the payment button on checkout", :js => true do + visit spree.checkout_state_path(:payment) + + # prevent form submit to verify button is disabled + page.execute_script("$('#checkout_form_payment').submit(function(){return false;})") + + page.should_not have_selector('input.button[disabled]') + click_button "Save and Continue" + page.should have_selector('input.button[disabled]') + end + + it "prevents double clicking the confirm button on checkout", :js => true do + visit spree.checkout_state_path(:confirm) + + # prevent form submit to verify button is disabled + page.execute_script("$('#checkout_form_confirm').submit(function(){return false;})") + + page.should_not have_selector('input.button[disabled]') + click_button "Place Order" + page.should have_selector('input.button[disabled]') + end # Regression test for #1596 context "full checkout" do before do - Factory(:payment_method) + create(:payment_method) Spree::ShippingMethod.delete_all - shipping_method = Factory(:shipping_method) + shipping_method = create(:shipping_method) calculator = Spree::Calculator::PerItem.create!({:calculable => shipping_method}, :without_protection => true) shipping_method.calculator = calculator shipping_method.save @@ -51,7 +91,7 @@ visit spree.root_path click_link "RoR Mug" click_button "add-to-cart-button" - click_link "Checkout" + click_button "Checkout" Spree::Order.last.update_column(:email, "ryan@spreecommerce.com") address = "order_bill_address_attributes" diff --git a/core/spec/spec_helper.rb b/core/spec/spec_helper.rb index b9cf369b504..1bcf9189de8 100644 --- a/core/spec/spec_helper.rb +++ b/core/spec/spec_helper.rb @@ -9,9 +9,13 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} require 'database_cleaner' + require 'spree/core/testing_support/factories' -require 'spree/core/testing_support/env' require 'spree/core/testing_support/controller_requests' +require 'spree/core/testing_support/authorization_helpers' +require 'spree/core/testing_support/preferences' +require 'spree/core/testing_support/flash' + require 'spree/core/url_helpers' require 'paperclip/matchers' @@ -53,6 +57,8 @@ config.include FactoryGirl::Syntax::Methods config.include Spree::Core::UrlHelpers config.include Spree::Core::TestingSupport::ControllerRequests + config.include Spree::Core::TestingSupport::Preferences + config.include Spree::Core::TestingSupport::Flash config.include Paperclip::Shoulda::Matchers end diff --git a/core/spec/support/authorization_helpers.rb b/core/spec/support/authorization_helpers.rb deleted file mode 100644 index 48fd74518a2..00000000000 --- a/core/spec/support/authorization_helpers.rb +++ /dev/null @@ -1,30 +0,0 @@ -module AuthorizationHelpers - module Controller - def stub_authorization! - before do - controller.should_receive(:authorize!).twice.and_return(true) - end - end - end - - module Request - class SuperAbility - include CanCan::Ability - - def initialize(user) - # allow anyone to perform index on Order - can :manage, :all - end - end - - def stub_authorization! - before(:all) { Spree::Ability.register_ability(AuthorizationHelpers::Request::SuperAbility) } - after(:all) { Spree::Ability.remove_ability(AuthorizationHelpers::Request::SuperAbility) } - end - end -end - -RSpec.configure do |config| - config.extend AuthorizationHelpers::Controller, :type => :controller - config.extend AuthorizationHelpers::Request, :type => :request -end diff --git a/core/spec/support/capybara_ext.rb b/core/spec/support/capybara_ext.rb index cdc21ee7cee..25c54737862 100644 --- a/core/spec/support/capybara_ext.rb +++ b/core/spec/support/capybara_ext.rb @@ -8,9 +8,14 @@ def select2(within, value) script = %Q{ $('#{within} .select2-search-field input').val('#{value}') $('#{within} .select2-search-field input').keydown(); - $('#{within} .select2-highlighted').click(); } page.execute_script(script) + + # Wait for list to populate... + wait_until do + page.find(".select2-highlighted").visible? + end + page.execute_script("$('.select2-highlighted').mouseup();") end end diff --git a/core/spec/support/gc_hacks.rb b/core/spec/support/gc_hacks.rb deleted file mode 100644 index 5fe23567cb5..00000000000 --- a/core/spec/support/gc_hacks.rb +++ /dev/null @@ -1,19 +0,0 @@ -# From: http://www.rubyinside.com/careful-cutting-to-get-faster-rspec-runs-with-rails-5207.html -counter = -1 -RSpec.configure do |config| - config.after(:each) do - unless RUBY_VERSION =~ /1\.9\.2/ - counter += 1 - if counter > 9 - GC.enable - GC.start - GC.disable - counter = 0 - end - end - end - - config.after(:suite) do - counter = 0 - end -end diff --git a/core/spec/support/preferences.rb b/core/spec/support/preferences.rb deleted file mode 100644 index ba1731ba34f..00000000000 --- a/core/spec/support/preferences.rb +++ /dev/null @@ -1,14 +0,0 @@ -# Resets all preferences to default values, you can -# pass a block to override the defaults with a block -# -# reset_spree_preferences do |config| -# config.site_name = "my fancy pants store" -# end -# -def reset_spree_preferences - Spree::Preferences::Store.instance.persistence = false - config = Rails.application.config.spree.preferences - config.reset - yield(config) if block_given? -end - diff --git a/core/spree_core.gemspec b/core/spree_core.gemspec index b02cb719c44..396f791df9f 100644 --- a/core/spree_core.gemspec +++ b/core/spree_core.gemspec @@ -19,21 +19,23 @@ Gem::Specification.new do |s| s.requirements << 'none' s.add_dependency 'acts_as_list', '= 0.1.4' - s.add_dependency 'nested_set', '= 1.7.0' + s.add_dependency 'awesome_nested_set', '2.1.4' - s.add_dependency 'jquery-rails', '~> 2.0.0' - s.add_dependency 'select2-rails', '0.0.9' + s.add_dependency 'jquery-rails', '~> 2.0' + s.add_dependency 'select2-rails', '~> 3.0' s.add_dependency 'highline', '= 1.6.11' s.add_dependency 'state_machine', '= 1.1.2' s.add_dependency 'ffaker', '~> 1.12.0' - s.add_dependency 'paperclip', '~> 2.7' + s.add_dependency 'paperclip', '~> 2.8' s.add_dependency 'aws-sdk', '~> 1.3.4' - s.add_dependency 'ransack', '~> 0.6.0' - s.add_dependency 'activemerchant', '= 1.20.4' - s.add_dependency 'rails', '~> 3.2.7' - s.add_dependency 'kaminari', '>= 0.13.0' + s.add_dependency 'ransack', '~> 0.7.0' + s.add_dependency 'activemerchant', '= 1.28.0' + s.add_dependency 'rails', '~> 3.2.9' + s.add_dependency 'kaminari', '0.13.0' s.add_dependency 'deface', '>= 0.9.0' s.add_dependency 'stringex', '~> 1.3.2' s.add_dependency 'cancan', '1.6.7' + s.add_dependency 'money', '5.0.0' + s.add_dependency 'rabl', '0.7.2' end diff --git a/dash/app/overrides/analytics_header.rb b/dash/app/overrides/analytics_header.rb index a4682bf1ab8..af580f245c6 100644 --- a/dash/app/overrides/analytics_header.rb +++ b/dash/app/overrides/analytics_header.rb @@ -1,5 +1,5 @@ -Deface::Override.new(:virtual_path => "spree/layouts/spree_application", +Deface::Override.new(:virtual_path => Spree::Config[:layout], :name => "add_analytics_header", - :insert_bottom => "[data-hook='inside_head']", + :insert_bottom => "head", :partial => "spree/analytics/header", :original => '6f23c8af6e863d0499835c00b3f2763cb98e1d75') diff --git a/dash/app/views/spree/admin/analytics/sign_up.html.erb b/dash/app/views/spree/admin/analytics/sign_up.html.erb index 794095e6006..dd4efe66ab1 100644 --- a/dash/app/views/spree/admin/analytics/sign_up.html.erb +++ b/dash/app/views/spree/admin/analytics/sign_up.html.erb @@ -3,11 +3,11 @@ <%= t(:analytics_sign_up) %>

      <%= check_box_tag 'store[terms_of_service]', @store[:terms_of_service], @store.has_key?(:terms_of_service), :size => 50 %> - <%= label_tag 'store[terms_of_service]', t(:agree_to_terms_of_service) %> (<%= t(:review) %>) + <%= label_tag 'store[terms_of_service]', t(:agree_to_terms_of_service) %> (<%= t(:review) %>)

      <%= check_box_tag 'store[privacy_policy]', @store[:privacy_policy], @store.has_key?(:privacy_policy), :size => 50 %> - <%= label_tag 'store[privacy_policy]', t(:agree_to_privacy_policy) %> (<%= t(:review) %>) + <%= label_tag 'store[privacy_policy]', t(:agree_to_privacy_policy) %> (<%= t(:review) %>)

      <%= label_tag :first_name %>
      diff --git a/dash/spec/spec_helper.rb b/dash/spec/spec_helper.rb index cf2796681c3..5667cb90ace 100644 --- a/dash/spec/spec_helper.rb +++ b/dash/spec/spec_helper.rb @@ -12,7 +12,6 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} require 'spree/core/testing_support/factories' -require 'spree/core/testing_support/env' require 'spree/core/testing_support/controller_requests' require 'active_record/fixtures' diff --git a/install.rb b/install.rb index 72c64ef5364..3a8ad36260c 100644 --- a/install.rb +++ b/install.rb @@ -4,13 +4,13 @@ %w( core api dash promo sample ).each do |framework| puts "Installing #{framework}..." - + Dir.chdir(framework) do `gem build spree_#{framework}.gemspec` `gem install spree_#{framework}-#{version}.gem --no-ri --no-rdoc` FileUtils.remove "spree_#{framework}-#{version}.gem" end - + end puts "Installing Spree..." diff --git a/lib/sandbox.sh b/lib/sandbox.sh index b4a79f206e3..3944652ef35 100755 --- a/lib/sandbox.sh +++ b/lib/sandbox.sh @@ -4,7 +4,6 @@ rm -rf sandbox rails new sandbox --skip-bundle cd sandbox echo "gem 'spree', :path => '..'" >> Gemfile -echo "gem 'spree_auth_devise', :git => 'git://github.com/spree/spree_auth_devise'" >> Gemfile +echo "gem 'spree_auth_devise', :github => 'spree/spree_auth_devise', :branch => '1-2-stable'" >> Gemfile bundle install --gemfile Gemfile -rails g spree:install --auto-accept -rake assets:precompile +rails g spree:install --auto-accept --user_class=Spree::User diff --git a/promo/app/assets/javascripts/admin/spree_promo.js b/promo/app/assets/javascripts/admin/spree_promo.js index 451b28e682b..73a06727fbf 100644 --- a/promo/app/assets/javascripts/admin/spree_promo.js +++ b/promo/app/assets/javascripts/admin/spree_promo.js @@ -1,2 +1,37 @@ //= require admin/spree_core //= require_tree . + +function cleanUsers(data) { + var users = $.map(data['users'], function(result) { + return result['user'] + }) + return users; +} + +$(document).ready(function() { + $('.user_picker').select2({ + minimumInputLength: 1, + multiple: true, + initSelection: function(element, callback) { + $.get(Spree.routes.user_search, { ids: element.val() }, function(data) { + callback(cleanUsers(data)) + }) + }, + ajax: { + url: Spree.routes.user_search, + datatype: 'json', + data: function(term, page) { + return { q: term } + }, + results: function(data, page) { + return { results: cleanUsers(data) } + } + }, + formatResult: function(user) { + return user.email; + }, + formatSelection: function(user) { + return user.email; + } + }); +}) diff --git a/promo/app/controllers/spree/checkout_controller_decorator.rb b/promo/app/controllers/spree/checkout_controller_decorator.rb index 3fd876be623..412b35f7269 100644 --- a/promo/app/controllers/spree/checkout_controller_decorator.rb +++ b/promo/app/controllers/spree/checkout_controller_decorator.rb @@ -8,9 +8,7 @@ def update if @order.coupon_code.present? event_name = "spree.checkout.coupon_code_added" - if Spree::Promotion.exists?(:code => @order.coupon_code, - :event_name => event_name) - + if promo = Spree::Promotion.with_coupon_code(@order.coupon_code).where(:event_name => event_name).first 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 diff --git a/promo/app/controllers/spree/orders_controller_decorator.rb b/promo/app/controllers/spree/orders_controller_decorator.rb index f3910df0fc4..7b3dda46fb0 100644 --- a/promo/app/controllers/spree/orders_controller_decorator.rb +++ b/promo/app/controllers/spree/orders_controller_decorator.rb @@ -13,7 +13,15 @@ def update 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 } } + respond_with(@order) do |format| + format.html do + if params.has_key?(:checkout) + redirect_to checkout_state_path(@order.checkout_steps.first) + else + redirect_to cart_path + end + end + end else respond_with(@order) end @@ -21,9 +29,11 @@ def update def apply_coupon_code return if @order.coupon_code.blank? - if Spree::Promotion.exists?(:code => @order.coupon_code) - fire_event('spree.checkout.coupon_code_added', :coupon_code => @order.coupon_code) - true + if promo = Spree::Promotion.with_coupon_code(@order.coupon_code).first + if promo.order_activatable?(@order) + fire_event('spree.checkout.coupon_code_added', :coupon_code => @order.coupon_code) + true + end end end diff --git a/promo/app/models/spree/adjustment_decorator.rb b/promo/app/models/spree/adjustment_decorator.rb index ef2023df4ab..36fd9dde532 100644 --- a/promo/app/models/spree/adjustment_decorator.rb +++ b/promo/app/models/spree/adjustment_decorator.rb @@ -1,3 +1,7 @@ Spree::Adjustment.class_eval do - scope :promotion, lambda { where('label LIKE ?', "#{I18n.t(:promotion)}%") } + class << self + def promotion + where(:originator_type => 'Spree::PromotionAction') + end + end end diff --git a/promo/app/models/spree/calculator/percent_per_item.rb b/promo/app/models/spree/calculator/percent_per_item.rb new file mode 100644 index 00000000000..d49bfa2ae22 --- /dev/null +++ b/promo/app/models/spree/calculator/percent_per_item.rb @@ -0,0 +1,48 @@ +module Spree + + # A calculator for promotions that calculates a percent-off discount + # for all matching products in an order. This should not be used as a + # shipping calculator since it would be the same thing as a flat percent + # off the entire order. + + class Calculator::PercentPerItem < Calculator + preference :percent, :decimal, :default => 0 + + attr_accessible :preferred_percent + + def self.description + I18n.t(:percent_per_item) + end + + def compute(object=nil) + return 0 if object.nil? + object.line_items.reduce(0) do |sum, line_item| + sum += value_for_line_item(line_item) + end + end + + private + + # Returns all products that match the promotion's rule. + def matching_products + @matching_products ||= if compute_on_promotion? + self.calculable.promotion.rules.map(&:products).flatten + end + end + + # Calculates the discount value of each line item. Returns zero + # unless the product is included in the promotion rules. + def value_for_line_item(line_item) + if compute_on_promotion? + return 0 unless matching_products.include?(line_item.product) + end + line_item.price * line_item.quantity * preferred_percent + end + + # Determines wether or not the calculable object is a promotion + def compute_on_promotion? + @compute_on_promotion ||= self.calculable.respond_to?(:promotion) + end + + end +end diff --git a/promo/app/models/spree/order_decorator.rb b/promo/app/models/spree/order_decorator.rb index 04c48d0d746..cfa30399309 100644 --- a/promo/app/models/spree/order_decorator.rb +++ b/promo/app/models/spree/order_decorator.rb @@ -16,11 +16,13 @@ def update_adjustments_with_promotion_limiting 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| - if adjustment.originator.calculator.is_a?(Spree::Calculator::PerItem) - adjustment.update_attribute_without_callbacks(:eligible, false) - end + adjustment.update_attribute_without_callbacks(:eligible, false) end end alias_method_chain :update_adjustments, :promotion_limiting end + + def promo_total + adjustments.eligible.promotion.map(&:amount).sum + end end diff --git a/promo/app/models/spree/payment_decorator.rb b/promo/app/models/spree/payment_decorator.rb new file mode 100644 index 00000000000..4abcae150fa --- /dev/null +++ b/promo/app/models/spree/payment_decorator.rb @@ -0,0 +1,5 @@ +Spree::Payment.class_eval do + def promo_total + order.promo_total * 100 + end +end diff --git a/promo/app/models/spree/product_decorator.rb b/promo/app/models/spree/product_decorator.rb index 8a81786e580..ccf3345eff6 100644 --- a/promo/app/models/spree/product_decorator.rb +++ b/promo/app/models/spree/product_decorator.rb @@ -2,8 +2,7 @@ has_and_belongs_to_many :promotion_rules, :join_table => :spree_products_promotion_rules def possible_promotions - all_rules = promotion_rules - promotion_ids = all_rules.map(&:activator_id).uniq - Spree::Promotion.advertised.where(:id => promotion_ids) + promotion_ids = promotion_rules.map(&:activator_id).uniq + Spree::Promotion.advertised.where(:id => promotion_ids).reject(&:expired?) end end diff --git a/promo/app/models/spree/promotion.rb b/promo/app/models/spree/promotion.rb index 6c306504b81..c9b76619dbb 100644 --- a/promo/app/models/spree/promotion.rb +++ b/promo/app/models/spree/promotion.rb @@ -36,12 +36,8 @@ def self.advertised where(:advertise => true) end - # TODO: Remove that after fix for https://rails.lighthouseapp.com/projects/8994/tickets/4329-has_many-through-association-does-not-link-models-on-association-save - # is provided - def save(*) - if super - promotion_rules.each(&:save) - end + def self.with_coupon_code(coupon_code) + search(:code_cont => coupon_code).result end def activate(payload) diff --git a/promo/app/models/spree/promotion/rules/item_total.rb b/promo/app/models/spree/promotion/rules/item_total.rb index 971b9210058..bd47243c53c 100644 --- a/promo/app/models/spree/promotion/rules/item_total.rb +++ b/promo/app/models/spree/promotion/rules/item_total.rb @@ -1,4 +1,5 @@ -# A rule to limit a promotion to a specific user. +# A rule to apply to an order greater than (or greater than or equal to) +# a specific amount module Spree class Promotion module Rules diff --git a/promo/app/views/spree/admin/promotions/_form.html.erb b/promo/app/views/spree/admin/promotions/_form.html.erb index 1d02632691f..e64ffb89e2e 100644 --- a/promo/app/views/spree/admin/promotions/_form.html.erb +++ b/promo/app/views/spree/admin/promotions/_form.html.erb @@ -37,7 +37,8 @@ <%= t(:expiry) %>

      <%= f.label :usage_limit %>
      - <%= f.text_field :usage_limit %> + <%= f.text_field :usage_limit %>
      + <%= t(:current_promotion_usage, :count => @promotion.credits_count) %>

      diff --git a/promo/app/views/spree/admin/promotions/rules/_user.html.erb b/promo/app/views/spree/admin/promotions/rules/_user.html.erb index d777810cffa..d09610ecf1b 100644 --- a/promo/app/views/spree/admin/promotions/rules/_user.html.erb +++ b/promo/app/views/spree/admin/promotions/rules/_user.html.erb @@ -1,8 +1,4 @@ -

      - -

      +
      +
      + +
      diff --git a/promo/app/views/spree/checkout/_coupon_code_field.html.erb b/promo/app/views/spree/checkout/_coupon_code_field.html.erb index 2e55f0daa99..5b0f3aec58f 100644 --- a/promo/app/views/spree/checkout/_coupon_code_field.html.erb +++ b/promo/app/views/spree/checkout/_coupon_code_field.html.erb @@ -1,6 +1,6 @@ <% if Spree::Promotion.count > 0 %> -

      - <%= form.label :coupon_code %>
      - <%= form.text_field :coupon_code %> -

      +

      + <%= form.label :coupon_code %>
      + <%= form.text_field :coupon_code %> +

      <% end %> diff --git a/promo/config/locales/en.yml b/promo/config/locales/en.yml index a39b162b620..24e0d3d2576 100644 --- a/promo/config/locales/en.yml +++ b/promo/config/locales/en.yml @@ -18,6 +18,7 @@ en: coupon_code: Coupon code coupon_code_applied: The coupon code was successfully applied to your order. editing_promotion: Editing Promotion + current_promotion_usage: 'Current Usage: %{count}' events: spree: checkout: @@ -34,6 +35,7 @@ en: path: Path new_promotion: New Promotion no_rules_added: No rules added + percent_per_item: Percent Per Item product_rule: choose_products: Choose products label: "Order must contain %{select} of these products" diff --git a/promo/lib/spree/promo/engine.rb b/promo/lib/spree/promo/engine.rb index d93dad44dac..2ce1ac1a148 100644 --- a/promo/lib/spree/promo/engine.rb +++ b/promo/lib/spree/promo/engine.rb @@ -33,6 +33,7 @@ def default_notification_payload Spree::Calculator::FlatRate, Spree::Calculator::FlexiRate, Spree::Calculator::PerItem, + Spree::Calculator::PercentPerItem, Spree::Calculator::FreeShipping ] end diff --git a/promo/spec/controllers/spree/orders_controller_spec.rb b/promo/spec/controllers/spree/orders_controller_spec.rb index 65133c7dcc7..5546363073a 100644 --- a/promo/spec/controllers/spree/orders_controller_spec.rb +++ b/promo/spec/controllers/spree/orders_controller_spec.rb @@ -4,7 +4,17 @@ let(:user) { create(:user) } let(:order) { user.spree_orders.create } - let(:promotion) { Spree::Promotion.create(:name => "TestPromo", :code => "TEST1", :expires_at => Time.now + 86400, :usage_limit => 99, :event_name => "spree.checkout.coupon_code_added", :match_policy => "any") } + let(:promotion) do + Spree::Promotion.create({ + :name => "TestPromo", + :code => "TEST1", + :expires_at => 1.day.from_now, + :created_at => 1.day.ago, + :event_name => "spree.checkout.coupon_code_added", + :match_policy => "any" + }, :without_protection => true) + end + let(:coupon_code) { promotion.code } let(:invalid_coupon_code) { "12345" } diff --git a/promo/spec/models/calculator/percent_per_item_spec.rb b/promo/spec/models/calculator/percent_per_item_spec.rb new file mode 100644 index 00000000000..75b23e8da91 --- /dev/null +++ b/promo/spec/models/calculator/percent_per_item_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe Spree::Calculator::PercentPerItem do + # Like an order object, but not quite... + let!(:product1) { double("Product") } + let!(:product2) { double("Product") } + let!(:line_items) { [double("LineItem", :quantity => 5, :product => product1, :price => 10), double("LineItem", :quantity => 1, :product => product2, :price => 10)] } + let!(:object) { double("Order", :line_items => line_items) } + + let!(:promotion_calculable) { double("Calculable", :promotion => promotion) } + + let!(:promotion) { double("Promotion", :rules => [double("Rule", :products => [product1])]) } + + let!(:calculator) { Spree::Calculator::PercentPerItem.new(:preferred_percent => 0.25) } + + it "has a translation for description" do + calculator.description.should_not include("translation missing") + calculator.description.should == I18n.t(:percent_per_item) + end + + it "correctly calculates per item promotion" do + calculator.stub(:calculable => promotion_calculable) + calculator.compute(object).to_f.should == 12.5 # 5 x 10 x 0.25 since only product1 is included in the promotion rule + end + + it "returns 0 when no object passed" do + calculator.stub(:calculable => promotion_calculable) + calculator.compute.should == 0 + end + + it "computes on promotion when promotion is present" do + calculator.send(:compute_on_promotion?).should_not be_true + calculator.stub(:calculable => promotion_calculable) + calculator.send(:compute_on_promotion?).should be_true + end + +end diff --git a/promo/spec/models/order_spec.rb b/promo/spec/models/order_spec.rb index 629ade3eba1..4ebcd50b395 100644 --- a/promo/spec/models/order_spec.rb +++ b/promo/spec/models/order_spec.rb @@ -25,10 +25,15 @@ def create_adjustment(label, amount) create_adjustment("Promotion A", -100) create_adjustment("Promotion B", -200) create_adjustment("Promotion C", -300) - create_adjustment("Some other credit", -500) + create(:adjustment, :adjustable => order, + :originator => nil, + :amount => -500, + :locked => true, + :label => "Some other credit") order.adjustments.each {|a| a.update_attribute_without_callbacks(:eligible, true)} order.send(:update_adjustments) + order.adjustments.eligible.promotion.count.should == 1 order.adjustments.eligible.promotion.first.label.should == 'Promotion C' end @@ -43,6 +48,17 @@ def create_adjustment(label, amount) order.adjustments.eligible.promotion.first.amount.to_i.should == -200 end + it "should only include eligible adjustments in promo_total" do + create_adjustment("Promotion A", -100) + create(:adjustment, :adjustable => order, + :originator => nil, + :amount => -1000, + :locked => true, + :eligible => false, + :label => 'Bad promo') + + order.promo_total.to_f.should == -100.to_f + end end end diff --git a/promo/spec/models/payment_spec.rb b/promo/spec/models/payment_spec.rb new file mode 100644 index 00000000000..69bb7869777 --- /dev/null +++ b/promo/spec/models/payment_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +module Spree + describe Payment do + + # Regression test for feature introduced in #1956 + # Previous implementation caused it to stack level too deep + it "does not stack level too deep when asked for gateway options" do + order = stub_model(Order, :promo_total => 1) + payment = stub_model(Payment, :order => order) + + payment.gateway_options[:discount].should == 100 + end + end +end diff --git a/promo/spec/models/promotion/rules/item_total_spec.rb b/promo/spec/models/promotion/rules/item_total_spec.rb index ba8db09ace9..09c1db60eb5 100644 --- a/promo/spec/models/promotion/rules/item_total_spec.rb +++ b/promo/spec/models/promotion/rules/item_total_spec.rb @@ -2,8 +2,7 @@ describe Spree::Promotion::Rules::ItemTotal do let(:rule) { Spree::Promotion::Rules::ItemTotal.new } - let(:order) { mock_model(Spree::Order, :user => nil) } - # let(:order) { mock_model Order, :line_items => [mock_model(LineItem, :amount => 10), mock_model(LineItem, :amount => 20)] } + let(:order) { mock(:order) } before { rule.preferred_amount = 50 } @@ -11,19 +10,17 @@ before { rule.preferred_operator = 'gt' } it "should be eligible when item total is greater than preferred amount" do - # order.stub(:item_total => 51) - order.stub :line_items => [mock_model(Spree::LineItem, :amount => 30), mock_model(Spree::LineItem, :amount => 21)] + order.stub :line_items => [mock(:line_item, :amount => 30), mock(:line_item, :amount => 21)] rule.should be_eligible(order) end it "should not be eligible when item total is equal to preferred amount" do - # order.stub(:item_total => 50) - order.stub :line_items => [mock_model(Spree::LineItem, :amount => 30), mock_model(Spree::LineItem, :amount => 20)] + order.stub :line_items => [mock(:line_item, :amount => 30), mock(:line_item, :amount => 20)] rule.should_not be_eligible(order) end it "should not be eligible when item total is lower than to preferred amount" do - order.stub :line_items => [mock_model(Spree::LineItem, :amount => 30), mock_model(Spree::LineItem, :amount => 19)] + order.stub :line_items => [mock(:line_item, :amount => 30), mock(:line_item, :amount => 19)] rule.should_not be_eligible(order) end end @@ -32,17 +29,17 @@ before { rule.preferred_operator = 'gte' } it "should be eligible when item total is greater than preferred amount" do - order.stub :line_items => [mock_model(Spree::LineItem, :amount => 30), mock_model(Spree::LineItem, :amount => 21)] + order.stub :line_items => [mock(:line_item, :amount => 30), mock(:line_item, :amount => 21)] rule.should be_eligible(order) end it "should be eligible when item total is equal to preferred amount" do - order.stub :line_items => [mock_model(Spree::LineItem, :amount => 30), mock_model(Spree::LineItem, :amount => 20)] + order.stub :line_items => [mock(:line_item, :amount => 30), mock(:line_item, :amount => 20)] rule.should be_eligible(order) end it "should not be eligible when item total is lower than to preferred amount" do - order.stub :line_items => [mock_model(Spree::LineItem, :amount => 30), mock_model(Spree::LineItem, :amount => 19)] + order.stub :line_items => [mock(:line_item, :amount => 30), mock(:line_item, :amount => 19)] rule.should_not be_eligible(order) end end diff --git a/promo/spec/requests/checkout_spec.rb b/promo/spec/requests/checkout_spec.rb index 76ce20f3d6a..085cb1921dd 100644 --- a/promo/spec/requests/checkout_spec.rb +++ b/promo/spec/requests/checkout_spec.rb @@ -7,15 +7,16 @@ create(:zone) create(:shipping_method) create(:payment_method) - create(:promotion) end + let!(:promotion) { create(:promotion, :code => "onetwo") } + it "informs about an invalid coupon code", :js => true do visit spree.root_path click_link "RoR Mug" click_button "add-to-cart-button" - click_link "Checkout" + click_button "Checkout" fill_in "order_email", :with => "spree@example.com" click_button "Continue" @@ -38,7 +39,27 @@ click_button "Save and Continue" page.should have_content("The coupon code you entered doesn't exist. Please try again.") end - end -end + context "on the cart page" do + before do + visit spree.root_path + click_link "RoR Mug" + click_button "add-to-cart-button" + end + + it "cannot enter a promotion code that was created after the order" do + promotion.update_column(:created_at, 1.day.from_now) + fill_in "Coupon code", :with => "onetwo" + click_button "Apply" + page.should have_content("The coupon code you entered doesn't exist. Please try again.") + end + it "can enter a promotion code with both upper and lower case letters" do + promotion.update_column(:created_at, 1.minute.ago) + fill_in "Coupon code", :with => "ONETWO" + click_button "Apply" + page.should have_content("The coupon code was successfully applied to your order.") + end + end + end +end diff --git a/promo/spec/requests/promotion_adjustments_spec.rb b/promo/spec/requests/promotion_adjustments_spec.rb index d8f01f422b6..0956972ef10 100644 --- a/promo/spec/requests/promotion_adjustments_spec.rb +++ b/promo/spec/requests/promotion_adjustments_spec.rb @@ -5,9 +5,6 @@ context "coupon promotions", :js => true do before(:each) do - PAYMENT_STATES = Spree::Payment.state_machine.states.keys unless defined? PAYMENT_STATES - SHIPMENT_STATES = Spree::Shipment.state_machine.states.keys unless defined? SHIPMENT_STATES - ORDER_STATES = Spree::Order.state_machine.states.keys unless defined? ORDER_STATES # creates a default shipping method which is required for checkout create(:bogus_payment_method, :environment => 'test') # creates a check payment method so we don't need to worry about cc details @@ -27,6 +24,24 @@ let!(:address) { create(:address, :state => Spree::State.first) } + it "should properly populate Spree::Product#possible_promotions" do + promotion = create_per_product_promotion 'RoR Mug', 5.0 + promotion.update_column :advertise, true + + mug = Spree::Product.find_by_name 'RoR Mug' + bag = Spree::Product.find_by_name 'RoR Bag' + + mug.possible_promotions.size.should == 1 + bag.possible_promotions.size.should == 0 + + # expire the promotion + promotion.expires_at = Date.today.beginning_of_week + promotion.starts_at = Date.today.beginning_of_week.advance(:day => 3) + promotion.save! + + mug.possible_promotions.size.should == 0 + end + it "should allow an admin to create a flat rate discount coupon promo" do fill_in "Name", :with => "Order's total > $30" fill_in "Usage Limit", :with => "100" @@ -51,7 +66,7 @@ visit spree.root_path click_link "RoR Mug" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" fill_in "Customer E-Mail", :with => "spree@example.com" str_addr = "bill_address" @@ -97,7 +112,7 @@ visit spree.root_path click_link "RoR Mug" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" fill_in "Customer E-Mail", :with => "spree@example.com" str_addr = "bill_address" @@ -123,7 +138,7 @@ visit spree.root_path click_link "RoR Mug" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" fill_in "Customer E-Mail", :with => "spree@example.com" str_addr = "bill_address" @@ -147,7 +162,6 @@ it "should allow an admin to create an automatic promo with flat percent discount" do fill_in "Name", :with => "Order's total > $30" - fill_in "Code", :with => "" select "Order contents changed", :from => "Event" click_button "Create" page.should have_content("Editing Promotion") @@ -176,7 +190,6 @@ it "should allow an admin to create an automatic promotion with free shipping (no code)" do fill_in "Name", :with => "Free Shipping" - fill_in "Code", :with => "" click_button "Create" page.should have_content("Editing Promotion") @@ -193,7 +206,7 @@ visit spree.root_path click_link "RoR Bag" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" fill_in "Customer E-Mail", :with => "spree@example.com" str_addr = "bill_address" @@ -216,7 +229,7 @@ visit spree.root_path click_link "RoR Mug" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" str_addr = "bill_address" select "United States", :from => "order_#{str_addr}_attributes_country_id" @@ -317,7 +330,7 @@ visit spree.root_path click_link "RoR Bag" click_button "Add To Cart" - click_link "Checkout" + click_button "Checkout" str_addr = "bill_address" fill_in "order_email", :with => "buyer@spreecommerce.com" @@ -416,23 +429,26 @@ visit spree.root_path click_link "RoR Bag" click_button "Add To Cart" - Spree::Order.last.total.to_f.should == 13.00 + Spree::Order.last.total.to_f.should == 15.00 fill_in "order[line_items_attributes][0][quantity]", :with => "2" click_button "Update" - Spree::Order.last.total.to_f.should == 31.00 + Spree::Order.last.total.to_f.should == 35.00 fill_in "order[line_items_attributes][0][quantity]", :with => "3" click_button "Update" - Spree::Order.last.total.to_f.should == 49.00 + Spree::Order.last.total.to_f.should == 54.00 end - def create_per_product_promotion product_name, discount_amount + def create_per_product_promotion product_name, discount_amount, event = "Add to cart" + promotion_name = "Bundle d#{discount_amount}" + visit spree.admin_path click_link "Promotions" click_link "New Promotion" - fill_in "Name", :with => "Bundle" - select "Add to cart", :from => "Event" + + fill_in "Name", :with => promotion_name + select event, :from => "Event" click_button "Create" page.should have_content("Editing Promotion") @@ -454,6 +470,8 @@ def create_per_product_promotion product_name, discount_amount within('#actions_container') { click_button "Update" } within('.calculator-fields') { fill_in "Amount", :with => discount_amount.to_s } within('#actions_container') { click_button "Update" } + + Spree::Promotion.find_by_name promotion_name end def add_to_cart product_name @@ -463,7 +481,7 @@ def add_to_cart product_name end def do_checkout - click_link "Checkout" + click_button "Checkout" str_addr = "bill_address" fill_in "order_email", :with => "buyer@spreecommerce.com" select "United States", :from => "order_#{str_addr}_attributes_country_id" @@ -479,34 +497,5 @@ def do_checkout fill_in "card_code", :with => "123" click_button "Save and Continue" end - - def create_per_product_promotion product_name, discount_amount - visit spree.admin_path - click_link "Promotions" - click_link "New Promotion" - fill_in "Name", :with => "Bundle" - select "Add to cart", :from => "Event" - click_button "Create" - page.should have_content("Editing Promotion") - - # add product_name to last promotion - promotion = Spree::Promotion.last - promotion.rules << Spree::Promotion::Rules::Product.new() - product = Spree::Product.find_by_name(product_name) - rule = promotion.rules.last - rule.products << product - if rule.save - puts "Created promotion: new price for #{product_name} is #{product.price - discount_amount} (was #{product.price})" - else - puts "Failed to create promotion: price for #{product_name} is still #{product.price}" - end - - select "Create adjustment", :from => "Add action of type" - within('#action_fields') { click_button "Add" } - select "Flat Rate (per item)", :from => "Calculator" - within('#actions_container') { click_button "Update" } - within('.calculator-fields') { fill_in "Amount", :with => discount_amount.to_s } - within('#actions_container') { click_button "Update" } - end end end diff --git a/promo/spec/spec_helper.rb b/promo/spec/spec_helper.rb index 1ed7f954306..7c5e7d2d821 100644 --- a/promo/spec/spec_helper.rb +++ b/promo/spec/spec_helper.rb @@ -12,7 +12,7 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} require 'spree/core/testing_support/factories' -require 'spree/core/testing_support/env' +require 'spree/core/testing_support/authorization_helpers' require 'factories' require 'active_record/fixtures' diff --git a/sample/db/sample/spree/assets.yml b/sample/db/sample/spree/assets.yml index 39b957cf69b..9cb29da60b6 100644 --- a/sample/db/sample/spree/assets.yml +++ b/sample/db/sample/spree/assets.yml @@ -6,7 +6,7 @@ img_tote: attachment_file_name: ror_tote.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image + type: Spree::Image position: 1 img_tote_back: id: 2 @@ -16,7 +16,7 @@ img_tote_back: attachment_file_name: ror_tote_back.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image + type: Spree::Image position: 2 img_bag: id: 3 @@ -26,8 +26,8 @@ img_bag: attachment_file_name: ror_bag.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image - position: 1 + type: Spree::Image + position: 1 img_baseball: id: 4 viewable: ror_baseball_jersey_v @@ -36,7 +36,7 @@ img_baseball: attachment_file_name: ror_baseball.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_back: id: 5 @@ -46,7 +46,7 @@ img_baseball_back: attachment_file_name: ror_baseball_back.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image + type: Spree::Image position: 2 img_jr_spaghetti: id: 6 @@ -56,7 +56,7 @@ img_jr_spaghetti: attachment_file_name: ror_jr_spaghetti.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image + type: Spree::Image position: 1 img_mug: id: 7 @@ -66,7 +66,7 @@ img_mug: attachment_file_name: ror_mug.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image + type: Spree::Image position: 1 img_mug_back: id: 8 @@ -76,7 +76,7 @@ img_mug_back: attachment_file_name: ror_mug_back.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image + type: Spree::Image position: 2 img_ringer: id: 9 @@ -86,7 +86,7 @@ img_ringer: attachment_file_name: ror_ringer.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image + type: Spree::Image position: 1 img_ringer_back: id: 10 @@ -96,8 +96,8 @@ img_ringer_back: attachment_file_name: ror_ringer_back.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image - position: 2 + type: Spree::Image + position: 2 img_stein: id: 11 viewable: ror_stein_v @@ -136,8 +136,8 @@ img_ruby_baseball: attachment_file_name: ruby_baseball.png attachment_width: 495 attachment_height: 477 - type: Spree::Image - position: 1 + type: Spree::Image + position: 1 img_baseball_small_green: id: 1009 viewable: small-green-baseball @@ -146,7 +146,7 @@ img_baseball_small_green: attachment_file_name: ror_baseball_jersey_green.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_small_green_back: id: 1010 @@ -156,7 +156,7 @@ img_baseball_small_green_back: attachment_file_name: ror_baseball_jersey_back_green.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 2 img_baseball_med_green: id: 1011 @@ -166,7 +166,7 @@ img_baseball_med_green: attachment_file_name: ror_baseball_jersey_green.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_med_green_back: id: 1012 @@ -176,8 +176,8 @@ img_baseball_med_green_back: attachment_file_name: ror_baseball_jersey_back_green.png attachment_width: 240 attachment_height: 240 - type: Spree::Image - position: 2 + type: Spree::Image + position: 2 img_baseball_large_green: id: 1013 viewable: large-green-baseball @@ -186,7 +186,7 @@ img_baseball_large_green: attachment_file_name: ror_baseball_jersey_green.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_large_green_back: id: 1014 @@ -196,7 +196,7 @@ img_baseball_large_green_back: attachment_file_name: ror_baseball_jersey_back_green.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 2 img_baseball_small_blue: id: 1015 @@ -206,7 +206,7 @@ img_baseball_small_blue: attachment_file_name: ror_baseball_jersey_blue.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_small_blue_back: id: 1016 @@ -216,7 +216,7 @@ img_baseball_small_blue_back: attachment_file_name: ror_baseball_jersey_back_blue.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 2 img_baseball_med_blue: id: 1017 @@ -226,7 +226,7 @@ img_baseball_med_blue: attachment_file_name: ror_baseball_jersey_blue.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_med_blue_back: id: 1018 @@ -236,7 +236,7 @@ img_baseball_med_blue_back: attachment_file_name: ror_baseball_jersey_back_blue.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 2 img_baseball_large_blue: id: 1019 @@ -246,7 +246,7 @@ img_baseball_large_blue: attachment_file_name: ror_baseball_jersey_blue.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_large_blue_back: id: 1020 @@ -256,7 +256,7 @@ img_baseball_large_blue_back: attachment_file_name: ror_baseball_jersey_back_blue.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 2 img_baseball_large_red: id: 1021 @@ -266,7 +266,7 @@ img_baseball_large_red: attachment_file_name: ror_baseball_jersey_red.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_large_red_back: id: 1022 @@ -276,7 +276,7 @@ img_baseball_large_red_back: attachment_file_name: ror_baseball_jersey_back_red.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 2 img_baseball_med_red: id: 1023 @@ -286,7 +286,7 @@ img_baseball_med_red: attachment_file_name: ror_baseball_jersey_red.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_med_red_back: id: 1024 @@ -296,7 +296,7 @@ img_baseball_med_red_back: attachment_file_name: ror_baseball_jersey_back_red.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 2 img_baseball_small_red: id: 1025 @@ -306,7 +306,7 @@ img_baseball_small_red: attachment_file_name: ror_baseball_jersey_red.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 1 img_baseball_small_red_back: id: 1026 @@ -316,7 +316,7 @@ img_baseball_small_red_back: attachment_file_name: ror_baseball_jersey_back_red.png attachment_width: 240 attachment_height: 240 - type: Spree::Image + type: Spree::Image position: 2 img_spree_bag: id: 1027 @@ -326,8 +326,8 @@ img_spree_bag: attachment_file_name: spree_bag.jpeg attachment_width: 480 attachment_height: 480 - type: Spree::Image - position: 1 + type: Spree::Image + position: 1 img_spree_tote: id: 1028 viewable: spree_tote_v @@ -336,7 +336,7 @@ img_spree_tote: attachment_file_name: spree_tote_front.jpeg attachment_width: 480 attachment_height: 480 - type: Spree::Image + type: Spree::Image position: 1 img_tote_back: id: 1029 @@ -346,7 +346,7 @@ img_tote_back: attachment_file_name: spree_tote_back.jpeg attachment_width: 480 attachment_height: 480 - type: Spree::Image + type: Spree::Image position: 2 img_spree_ringer: id: 1030 @@ -356,7 +356,7 @@ img_spree_ringer: attachment_file_name: spree_ringer_t.jpeg attachment_width: 480 attachment_height: 480 - type: Spree::Image + type: Spree::Image position: 1 img_spree_ringer_back: id: 1031 @@ -366,8 +366,8 @@ img_spree_ringer_back: attachment_file_name: spree_ringer_t_back.jpeg attachment_width: 480 attachment_height: 480 - type: Spree::Image - position: 2 + type: Spree::Image + position: 2 img_spree_jr_spaghetti: id: 1032 viewable: spree_jr_spaghetti_v @@ -376,7 +376,7 @@ img_spree_jr_spaghetti: attachment_file_name: spree_spaghetti.jpeg attachment_width: 480 attachment_height: 480 - type: Spree::Image + type: Spree::Image position: 1 img_spree_stein: id: 1033 @@ -406,7 +406,7 @@ img_spree_mug: attachment_file_name: spree_mug.jpeg attachment_width: 360 attachment_height: 360 - type: Spree::Image + type: Spree::Image position: 1 img_spree_mug_back: id: 1036 @@ -416,7 +416,7 @@ img_spree_mug_back: attachment_file_name: spree_mug_back.jpeg attachment_width: 480 attachment_height: 480 - type: Spree::Image + type: Spree::Image position: 2 img_spree_baseball: id: 1037 @@ -426,7 +426,7 @@ img_spree_baseball: attachment_file_name: spree_jersey.jpeg attachment_width: 480 attachment_height: 480 - type: Spree::Image + type: Spree::Image position: 1 img_spree_baseball_back: id: 1038 @@ -436,5 +436,5 @@ img_spree_baseball_back: attachment_file_name: spree_jersey_back.jpeg attachment_width: 480 attachment_height: 480 - type: Spree::Image + type: Spree::Image position: 2 diff --git a/sample/db/sample/spree/payments.rb b/sample/db/sample/spree/payments.rb index b07cb687e0d..f0f27c2a0d9 100644 --- a/sample/db/sample/spree/payments.rb +++ b/sample/db/sample/spree/payments.rb @@ -8,6 +8,10 @@ def self.current end end +# This table was previously called spree_creditcards, and older migrations +# reference it as such. Make it explicit here that this table has been renamed. +Spree::CreditCard.table_name = 'spree_credit_cards' + creditcard = Spree::CreditCard.create({ :cc_type => 'visa', :month => 12, :year => 2014, :last_digits => '1111', :first_name => 'Sean', :last_name => 'Schofield', :gateway_customer_profile_id => 'BGS-1234' }, :without_protection => true) diff --git a/sample/db/sample/spree/users.yml b/sample/db/sample/spree/users.yml deleted file mode 100644 index 5bd748e2638..00000000000 --- a/sample/db/sample/spree/users.yml +++ /dev/null @@ -1,8 +0,0 @@ -<% -1.upto(100) do |i| -%> -user_<%= i %>: - email: <%= Faker::Internet.email %> - ship_address: ship_address_<%= i %> - bill_address: bill_address_<%= i %> -<% end %> \ No newline at end of file