Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: spree/spree
...
head fork: spree/spree
  • 4 commits
  • 20 files changed
  • 0 commit comments
  • 2 contributors
34 core/app/assets/javascripts/store/checkout.js.coffee
View
@@ -5,14 +5,24 @@
$ ->
if ($ '#checkout_form_address').is('*')
($ '#checkout_form_address').validate()
+
+ country_from_region = (region) ->
+ ($ 'p#' + region + 'country' + ' span#' + region + 'country-selection :only-child').val()
+
get_states = (region) ->
- country = ($ 'p#' + region + 'country' + ' span#' + region + 'country :only-child').val()
- state_mapper[country]
+ state_mapper[country_from_region(region)]
+
+ get_states_required = (region) ->
+ states_required_mapper[country_from_region(region)]
update_state = (region) ->
states = get_states(region)
- state_select = ($ 'p#' + region + 'state select')
- state_input = ($ 'p#' + region + 'state input')
+ states_required = get_states_required(region)
+
+ state_para = ($ 'p#' + region + 'state')
+ state_select = state_para.find('select')
+ state_input = state_para.find('input')
+ state_span_required = state_para.find('state-required')
if states
selected = parseInt state_select.val()
state_select.html ''
@@ -24,9 +34,17 @@ $ ->
state_select.prop('disabled', false).show()
state_input.hide().prop 'disabled', true
+ state_span_required.show()
else
- state_input.prop('disabled', false).show()
state_select.hide().prop 'disabled', true
+ state_input.show()
+ if states_required
+ state_span_required.show()
+ else
+ state_input.val ''
+ state_span_required.hide()
+ state_para.toggle(!!states_required)
+ state_input.prop('disabled', !states_required)
($ 'p#bcountry select').change ->
update_state 'b'
@@ -51,7 +69,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'
+ )
11 core/app/controllers/spree/admin/countries_controller.rb
View
@@ -0,0 +1,11 @@
+module Spree
+ module Admin
+ class CountriesController < ResourceController
+
+ def collection
+ super.order(:name)
+ end
+
+ end
+ end
+end
12 core/app/controllers/spree/countries_controller.rb
View
@@ -0,0 +1,12 @@
+module Spree
+ class CountriesController < BaseController
+ ssl_allowed :index
+
+ respond_to :js
+
+ def index
+ respond_with @states_required = Spree::Country.states_required_by_country_id.to_json, :layout => nil
+ end
+ end
+end
+
3  core/app/helpers/spree/admin/navigation_helper.rb
View
@@ -123,7 +123,8 @@ def configurations_menu_item(link_text, url, description = '')
end
def configurations_sidebar_menu_item(link_text, url, options = {})
- options.merge!(:class => url.include?(controller.controller_name) ? 'active' : nil)
+ is_active = url.ends_with?(controller.controller_name) || url.ends_with?( "#{controller.controller_name}/edit")
+ options.merge!(:class => is_active ? 'active' : nil)
content_tag(:li, options) do
link_to(link_text, url)
end
1  core/app/models/spree/address.rb
View
@@ -87,6 +87,7 @@ def state_validate
# Skip state validation without country (also required)
# or when disabled by preference
return if country.blank? || !Spree::Config[:address_requires_state]
+ return unless country.states_required
# ensure associated state belongs to country
if state.present?
11 core/app/models/spree/country.rb
View
@@ -4,6 +4,17 @@ class Country < ActiveRecord::Base
validates :name, :iso_name, :presence => true
+ attr_accessible :name, :iso_name, :states_required
+
+
+ def self.states_required_by_country_id
+ states_required = Hash.new(true)
+ self.all.each { |country|
+ states_required[country.id.to_s]= country.states_required
+ }
+ states_required
+ end
+
def <=>(other)
name <=> other.name
end
75 core/app/views/spree/address/_form.html.erb
View
@@ -0,0 +1,75 @@
+<% address_id = address_type.chars.first %>
+<div class="inner" data-hook=<%="#{address_type}_inner" %>>
+ <p class="field" id=<%="#{address_id}firstname" %>>
+ <%= form.label :firstname, t(:first_name) %><span class="required">*</span><br />
+ <%= form.text_field :firstname, :class => 'required' %>
+ </p>
+ <p class="field" id=<%="#{address_id}lastname" %>>
+ <%= form.label :lastname, t(:last_name) %><span class="required">*</span><br />
+ <%= form.text_field :lastname, :class => 'required' %>
+ </p>
+ <% if Spree::Config[:company] %>
+ <p class="field" id=<%="#{address_id}company" %>>
+ <%= form.label :company, t(:company) %><br />
+ <%= form.text_field :company %>
+ </p>
+ <% end %>
+ <p class="field" id=<%="#{address_id}address1" %>>
+ <%= form.label :address1, t(:street_address) %><span class="required">*</span><br />
+ <%= form.text_field :address1, :class => 'required' %>
+ </p>
+ <p class="field" id=<%="#{address_id}address2" %>>
+ <%= form.label :address2, t(:street_address_2) %><br />
+ <%= form.text_field :address2 %>
+ </p>
+ <p class="field" id=<%="#{address_id}city" %>>
+ <%= form.label :city, t(:city) %><span class="required">*</span><br />
+ <%= form.text_field :city, :class => 'required' %>
+ </p>
+ <p class="field" id=<%="#{address_id}country" %>>
+ <%= form.label :country_id, t(:country) %><span class="required">*</span><br />
+ <span id=<%="#{address_id}country-selection" %>>
+ <%= form.collection_select :country_id, available_countries, :id, :name, {}, {:class => 'required'} %>
+ </span>
+ </p>
+
+ <% if Spree::Config[:address_requires_state] %>
+ <p class="field" id=<%="#{address_id}state" %>>
+ <% have_states = !address.country.states.empty? %>
+ <%= form.label :state, t(:state) %><span class='required' id=<%="#{address_id}state-required"%>>*</span><br/>
+
+ <% state_elements = [
+ form.collection_select(:state_id, address.country.states,
+ :id, :name,
+ {:include_blank => true},
+ {:class => have_states ? 'required' : 'hidden',
+ :disabled => !have_states}) +
+ form.text_field(:state_name,
+ :class => !have_states ? 'required' : 'hidden',
+ :disabled => have_states)
+ ].join.gsub('"', "'").gsub("\n", "")
+ %>
+ <%= javascript_tag do -%>
+ document.write("<%== state_elements %>");
+ <% end %>
+ </p>
+ <noscript>
+ <%= form.text_field :state_name, :class => 'required' %>
+ </noscript>
+ <% end %>
+
+ <p class="field" id=<%="#{address_id}zipcode" %>>
+ <%= form.label :zipcode, t(:zip) %><span class="required">*</span><br />
+ <%= form.text_field :zipcode, :class => 'required' %>
+ </p>
+ <p class="field" id=<%="#{address_id}phone" %>>
+ <%= form.label :phone, t(:phone) %><span class="required">*</span><br />
+ <%= form.phone_field :phone, :class => 'required' %>
+ </p>
+ <% if Spree::Config[:alternative_shipping_phone] %>
+ <p class="field" id=<%="#{address_id}altphone" %>>
+ <%= form.label :alternative_phone, t(:alternative_phone) %><br />
+ <%= form.phone_field :alternative_phone %>
+ </p>
+ <% end %>
+</div>
17 core/app/views/spree/admin/countries/_form.html.erb
View
@@ -0,0 +1,17 @@
+<div data-hook="admin_country_form_fields" class="row">
+ <div class="alpha omega twelve columns">
+ <div data-hook="name" class="field">
+ <%= f.label :name, t(:name) %>
+ <%= f.text_field :name, :class => 'fullwidth' %>
+ </div>
+ <div data-hook="iso_name" class="field">
+ <%= f.label :iso_name, t(:iso_name) %>
+ <%= f.text_field :iso_name, :class => 'fullwidth' %>
+ </div>
+ <div data-hook="states_required" class="field">
+ <%= f.check_box :states_required %>
+ <%= f.label :states_required, t(:states_required) %><br>
+ </div>
+
+ </div>
+</div>
21 core/app/views/spree/admin/countries/edit.html.erb
View
@@ -0,0 +1,21 @@
+<%= render :partial => 'spree/admin/shared/configuration_menu' %>
+
+<% content_for :page_title do %>
+ <%= t(:editing_country) %>
+<% end %>
+
+<% content_for :page_actions do %>
+ <li>
+ <%= button_link_to t(:back_to_countries_list), spree.admin_countries_path, :icon => 'icon-arrow-left' %>
+ </li>
+<% end %>
+
+<%= render :partial => 'spree/shared/error_messages', :locals => { :target => @country } %>
+
+<%= form_for [:admin, @country] do |f| %>
+ <fieldset class="no-border-top">
+ <%= render :partial => 'form', :locals => { :f => f } %>
+ <div class="clear"></div>
+ <%= render :partial => 'spree/admin/shared/edit_resource_links' %>
+ </fieldset>
+<% end %>
33 core/app/views/spree/admin/countries/index.html.erb
View
@@ -0,0 +1,33 @@
+<%= render :partial => 'spree/admin/shared/configuration_menu' %>
+
+<% content_for :page_title do %>
+ <%= t(:listing_countries) %>
+<% end %>
+
+<table class="index" id='listing_countries' data-hook>
+ <colgroup>
+ <col style="width: 30%" />
+ <col style="width: 30%" />
+ <col style="width: 25%" />
+ <col style="width: 15%" />
+ </colgroup>
+ <thead>
+ <tr data-hook="tax_header">
+ <th><%= t(:name) %></th>
+ <th><%= t(:iso_name) %></th>
+ <th><%= t(:states_required) %></th>
+ <th class="actions"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <% @countries.each do |country| %>
+ <tr id="<%= spree_dom_id country %>" data-hook="country_row" class="<%= cycle('odd', 'even')%>">
+ <td><%= country.name %></td>
+ <td><%= country.iso_name %></td>
+ <td class="align-center"><%= country.states_required.to_s.titleize %></td>
+ <td class="actions">
+ <%= link_to_edit country, :no_text => true %>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
1  core/app/views/spree/admin/shared/_configuration_menu.html.erb
View
@@ -12,6 +12,7 @@
<%= configurations_sidebar_menu_item t(:tax_rates), admin_tax_rates_path %>
<%= configurations_sidebar_menu_item t(:tax_settings), edit_admin_tax_settings_path %>
<%= configurations_sidebar_menu_item t(:zones), admin_zones_path %>
+ <%= configurations_sidebar_menu_item t(:countries), admin_countries_path %>
<%= configurations_sidebar_menu_item t(:states), admin_country_states_path(Spree::Config[:default_country_id]) %>
<%= configurations_sidebar_menu_item t(:payment_methods), admin_payment_methods_path %>
<%= configurations_sidebar_menu_item t(:taxonomies), admin_taxonomies_path %>
182 core/app/views/spree/checkout/_address.html.erb
View
@@ -1,173 +1,25 @@
<div class="columns alpha six" data-hook="billing_fieldset_wrapper">
-<fieldset id="billing" data-hook>
- <%= form.fields_for :bill_address do |bill_form| %>
- <legend><%= t(:billing_address) %></legend>
- <div class="inner" data-hook="billing_inner">
- <p class="field" id="bfirstname">
- <%= bill_form.label :firstname, t(:first_name) %><span class="required">*</span><br />
- <%= bill_form.text_field :firstname, :class => 'required' %>
- </p>
- <p class="field" id="blastname">
- <%= bill_form.label :lastname, t(:last_name) %><span class="required">*</span><br />
- <%= bill_form.text_field :lastname, :class => 'required' %>
- </p>
- <% if Spree::Config[:company] %>
- <p class="field" id="bcompany">
- <%= bill_form.label :company, t(:company) %><br />
- <%= bill_form.text_field :company %>
- </p>
- <% end %>
- <p class="field" id="baddress1">
- <%= bill_form.label :address1, t(:street_address) %><span class="required">*</span><br />
- <%= bill_form.text_field :address1, :class => 'required' %>
- </p>
- <p class="field" id="baddress2">
- <%= bill_form.label :address2, t(:street_address_2) %><br />
- <%= bill_form.text_field :address2 %>
- </p>
-
- <p class="field" id="bcity">
- <%= bill_form.label :city, t(:city) %><span class="required">*</span><br />
- <%= bill_form.text_field :city, :class => 'required' %>
- </p>
-
- <p class="field" id="bcountry">
- <%= bill_form.label :country_id, t(:country) %><span class="required">*</span><br />
- <span id="bcountry">
- <%= bill_form.collection_select :country_id, available_countries, :id, :name, {}, {:class => 'required'} %>
- </span>
- </p>
-
- <% if Spree::Config[:address_requires_state] %>
- <p class="field" id="bstate">
- <% have_states = !@order.bill_address.country.states.empty? %>
- <%= bill_form.label :state, t(:state) %><span class="required">*</span><br />
- <% state_elements = [
- bill_form.collection_select(:state_id, @order.bill_address.country.states,
- :id, :name,
- {:include_blank => true},
- {:class => have_states ? 'required' : 'hidden',
- :disabled => !have_states}) +
- bill_form.text_field(:state_name,
- :class => !have_states ? 'required' : 'hidden',
- :disabled => have_states)
- ].join.gsub('"', "'").gsub("\n", "")
- %>
- <%= javascript_tag do -%>
- document.write("<%== state_elements %>");
- <% end -%>
- </p>
- <noscript>
- <%= bill_form.text_field :state_name, :class => 'required' %>
- </noscript>
- <% end %>
-
- <p class="field" id="bzipcode">
- <%= bill_form.label :zipcode, t(:zip) %><span class="required">*</span><br />
- <%= bill_form.text_field :zipcode, :class => 'required' %>
- </p>
- <p class="field" id="bphone">
- <%= bill_form.label :phone, t(:phone) %><span class="required">*</span><br />
- <%= bill_form.phone_field :phone, :class => 'required' %>
- </p>
- <% if Spree::Config[:alternative_billing_phone] %>
- <p class="field" id="baltphone">
- <%= bill_form.label :alternative_phone, t(:alternative_phone) %><br />
- <%= bill_form.phone_field :alternative_phone %>
- </p>
- <% end %>
- </div>
- <% end %>
-</fieldset>
+ <fieldset id="billing" data-hook>
+ <%= form.fields_for :bill_address do |bill_form| %>
+ <legend><%= t(:billing_address) %></legend>
+ <%= render :partial => 'spree/address/form', :locals => {:form => bill_form, :address_type => 'billing', :address => @order.bill_address} %>
+ <% end %>
+ </fieldset>
</div>
<div class="columns omega six" data-hook="shipping_fieldset_wrapper">
-<fieldset id="shipping" data-hook>
- <%= form.fields_for :ship_address do |ship_form| %>
- <legend><%= t(:shipping_address) %></legend>
- <p class="field checkbox" data-hook="use_billing">
- <%= check_box_tag 'order[use_billing]', '1', ((@order.bill_address.empty? && @order.ship_address.empty?) || @order.bill_address.same_as?(@order.ship_address)) %>
- <%= label_tag :order_use_billing, t(:use_billing_address), :id => 'use_billing' %>
- </p>
- <div class="inner" data-hook="shipping_inner">
- <p class="field" id="sfirstname">
- <%= ship_form.label :firstname, t(:first_name) %><span class="required">*</span><br />
- <%= ship_form.text_field :firstname, :class => 'required' %>
- </p>
- <p class="field" id="slastname">
- <%= ship_form.label :lastname, t(:last_name) %><span class="required">*</span><br />
- <%= ship_form.text_field :lastname, :class => 'required' %>
- </p>
- <% if Spree::Config[:company] %>
- <p class="field" id="scompany">
- <%= ship_form.label :company, t(:company) %><br />
- <%= ship_form.text_field :company %>
- </p>
- <% end %>
- <p class="field" id="saddress1">
- <%= ship_form.label :address1, t(:street_address) %><span class="required">*</span><br />
- <%= ship_form.text_field :address1, :class => 'required' %>
- </p>
- <p class="field" id="saddress2">
- <%= ship_form.label :address2, t(:street_address_2) %><br />
- <%= ship_form.text_field :address2 %>
- </p>
-
- <p class="field" id="scity">
- <%= ship_form.label :city, t(:city) %><span class="required">*</span><br />
- <%= ship_form.text_field :city, :class => 'required' %>
- </p>
-
- <p class="field" id="scountry">
- <%= ship_form.label :country_id, t(:country) %><span class="required">*</span><br />
- <span id="scountry">
- <%= ship_form.collection_select :country_id, available_countries, :id, :name, {}, {:class => 'required'} %>
- </span>
- </p>
-
- <% if Spree::Config[:address_requires_state] %>
- <p class="field" id="sstate">
- <% have_states = !@order.ship_address.country.states.empty? %>
- <%= ship_form.label :state, t(:state) %><span class="required">*</span><br />
- <% state_elements = [
- ship_form.collection_select(:state_id, @order.ship_address.country.states,
- :id, :name,
- {:include_blank => true},
- {:class => have_states ? 'required' : 'hidden',
- :disabled => !have_states}) +
- ship_form.text_field(:state_name,
- :class => !have_states ? 'required' : 'hidden',
- :disabled => have_states)
- ].join.gsub('"', "'").gsub("\n", "")
- %>
- <%= javascript_tag do -%>
- document.write("<%== state_elements %>");
- <% end %>
- </p>
- <noscript>
- <%= ship_form.text_field :state_name, :class => 'required' %>
- </noscript>
- <% end %>
-
- <p class="field" id="szipcode">
- <%= ship_form.label :zipcode, t(:zip) %><span class="required">*</span><br />
- <%= ship_form.text_field :zipcode, :class => 'required' %>
- </p>
- <p class="field" id="sphone">
- <%= ship_form.label :phone, t(:phone) %><span class="required">*</span><br />
- <%= ship_form.phone_field :phone, :class => 'required' %>
- </p>
- <% if Spree::Config[:alternative_shipping_phone] %>
- <p class="field" id="saltphone">
- <%= ship_form.label :alternative_phone, t(:alternative_phone) %><br />
- <%= ship_form.phone_field :alternative_phone %>
- </p>
- <% end %>
- </div>
- <% end %>
-</fieldset>
+ <fieldset id="shipping" data-hook>
+ <%= form.fields_for :ship_address do |ship_form| %>
+ <legend><%= t(:shipping_address) %></legend>
+ <p class="field checkbox" data-hook="use_billing">
+ <%= check_box_tag 'order[use_billing]', '1', ((@order.bill_address.empty? && @order.ship_address.empty?) || @order.bill_address.same_as?(@order.ship_address)) %>
+ <%= label_tag :order_use_billing, t(:use_billing_address), :id => 'use_billing' %>
+ </p>
+ <%= render :partial => 'spree/address/form', :locals => {:form => ship_form, :address_type => 'shipping', :address => @order.ship_address} %>
+ <% end %>
+ </fieldset>
</div>
-<hr class="clear" />
+<hr class="clear"/>
<div class="form-buttons" data-hook="buttons">
<%= submit_tag t(:save_and_continue), :class => 'continue button primary' %>
</div>
2  core/app/views/spree/checkout/edit.html.erb
View
@@ -1,6 +1,8 @@
<% content_for :head do %>
<%= javascript_include_tag states_url(:format => :js) %>
+ <%= javascript_include_tag countries_url(:format => :js) %>
<% end %>
+
<div id="checkout" data-hook>
<%= render :partial => 'spree/shared/error_messages', :locals => { :target => @order } %>
1  core/app/views/spree/countries/index.js.erb
View
@@ -0,0 +1 @@
+states_required_mapper = <%== @states_required %>
1  core/config/routes.rb
View
@@ -9,6 +9,7 @@
resources :tax_categories
resources :states, :only => :index
+ resources :countries, :only => :index
# non-restful checkout stuff
put '/checkout/update/:state', :to => 'checkout#update', :as => :update_checkout
5 core/db/migrate/20121010142909_add_states_required_to_countries.rb
View
@@ -0,0 +1,5 @@
+class AddStatesRequiredToCountries < ActiveRecord::Migration
+ def change
+ add_column :spree_countries, :states_required, :boolean,:default => true
+ end
+end
2  core/spec/models/address_spec.rb
View
@@ -46,7 +46,7 @@
end
end
- let(:country) { mock_model(Spree::Country, :states => [state]) }
+ let(:country) { mock_model(Spree::Country, :states => [state], :states_required => true) }
let(:state) { stub_model(Spree::State, :name => 'maryland', :abbr => 'md') }
let(:address) { FactoryGirl.build(:address, :country => country) }
10 core/spec/models/country_spec.rb
View
@@ -1,5 +1,15 @@
require 'spec_helper'
describe Spree::Country do
+ it "can find all countries group by states required" do
+ country_states_required= Spree::Country.create({:name => "Canada", :iso_name => "CAN", :states_required => true})
+ country_states_not_required= Spree::Country.create({:name => "France", :iso_name => "FR", :states_required => false})
+ states_required = Spree::Country.states_required_by_country_id
+ states_required[country_states_required.id.to_s].should be_true
+ states_required[country_states_not_required.id.to_s].should be_false
+ end
+ it "returns that the states are required for an invalid country" do
+ Spree::Country.states_required_by_country_id['i do not exit'].should be_true
+ end
end
58 core/spec/requests/address_spec.rb
View
@@ -0,0 +1,58 @@
+require 'spec_helper'
+
+describe "Address" do
+ let!(:canada) { create(:country, :name => "Canada",:states_required => true) }
+ let!(:france) { create(:country, :name => "France",:states_required => false) }
+ let!(:italy) { create(:country, :name => "Italy",:states_required => true) }
+
+ before(:all) do
+ Factory(:state, :name => "Ontario", :country => canada)
+ end
+
+ before do
+ Spree::Product.delete_all
+ @product = create(:product, :name => "RoR Mug", :on_hand => 1)
+ @product.save
+
+ @order = create(:order_with_totals, :state => 'cart')
+ @order.stub(:available_payment_methods => [create(:bogus_payment_method, :environment => 'test') ])
+
+ visit spree.root_path
+ click_link "RoR Mug"
+ click_button "add-to-cart-button"
+ Spree::Order.last.update_column(:email, "funk@groove.com")
+ click_button "Checkout"
+
+ address = "order_bill_address_attributes"
+ @country_css = "#{address}_country_id"
+ @state_select_css = "##{address}_state_id"
+ @state_name_css = "##{address}_state_name"
+ end
+
+ it "shows the state collection selection for a country having states", :js => true do
+ select canada.name, :from => @country_css
+ page.find(@state_select_css).should be_visible
+ page.find(@state_name_css).should_not be_visible
+ end
+
+ it "shows the state input field for a country with states required but for which states are not defined", :js => true do
+ select italy.name, :from => @country_css
+ page.find(@state_select_css).should_not be_visible
+ page.find(@state_name_css).should be_visible
+ page.should_not have_selector("input#{@state_name_css}[disabled]")
+ end
+
+ it "shows a disabled state input field for a country where states are not required", :js => true do
+ select france.name, :from => @country_css
+ page.find(@state_select_css).should_not be_visible
+ page.find(@state_name_css).should_not be_visible
+ end
+
+ it "should clear the state name when selecting a country without states required", :js =>true do
+ select italy.name, :from => @country_css
+ page.find(@state_name_css).set("Toscana")
+
+ select france.name, :from => @country_css
+ page.find(@state_name_css).should have_content('')
+ end
+end
2  core/spec/requests/checkout_spec.rb
View
@@ -1,7 +1,7 @@
require 'spec_helper'
describe "Checkout" do
- let(:country) { create(:country, :name => "Kangaland") }
+ let(:country) { create(:country, :name => "Kangaland",:states_required => true) }
before do
create(:state, :name => "Victoria", :country => country)
end

No commit comments for this range

Something went wrong with that request. Please try again.