spree_core 1.3.0.rc1 → 1.3.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/app/assets/javascripts/admin/admin.js.erb +28 -2
  2. data/app/assets/javascripts/admin/checkouts/edit.js +52 -96
  3. data/app/assets/javascripts/admin/handlebar_extensions.js +9 -0
  4. data/app/assets/javascripts/admin/spree-select2.js.erb +17 -19
  5. data/app/assets/javascripts/admin/spree_core.js +0 -1
  6. data/app/assets/javascripts/admin/states.js +9 -0
  7. data/app/assets/javascripts/admin/taxon_autocomplete.js.erb +1 -1
  8. data/app/assets/javascripts/admin/variant_autocomplete.js.erb +2 -6
  9. data/app/assets/javascripts/store/checkout.js.coffee +4 -4
  10. data/app/assets/stylesheets/store/screen.css.scss +8 -0
  11. data/app/controllers/spree/admin/option_values_controller.rb +11 -0
  12. data/app/controllers/spree/admin/product_properties_controller.rb +12 -0
  13. data/app/controllers/spree/admin/products_controller.rb +9 -0
  14. data/app/helpers/spree/admin/base_helper.rb +2 -8
  15. data/app/models/spree/app_configuration.rb +1 -0
  16. data/app/models/spree/calculator/per_item.rb +3 -1
  17. data/app/models/spree/line_item.rb +2 -2
  18. data/app/models/spree/order/checkout.rb +4 -1
  19. data/app/models/spree/order.rb +5 -1
  20. data/app/models/spree/payment/processing.rb +1 -1
  21. data/app/models/spree/preferences/preferable_class_methods.rb +6 -9
  22. data/app/models/spree/preferences/store.rb +22 -11
  23. data/app/models/spree/product/scopes.rb +5 -1
  24. data/app/models/spree/shipment.rb +7 -4
  25. data/app/models/spree/shipping_method.rb +4 -0
  26. data/app/views/spree/admin/orders/customer_details/_autocomplete.js.erb +19 -0
  27. data/app/views/spree/admin/orders/customer_details/edit.html.erb +4 -3
  28. data/app/views/spree/admin/orders/edit.html.erb +1 -1
  29. data/app/views/spree/admin/products/search.rabl +6 -0
  30. data/app/views/spree/admin/search/users.rabl +23 -25
  31. data/app/views/spree/admin/shared/_head.html.erb +1 -1
  32. data/app/views/spree/admin/shared/_routes.html.erb +3 -2
  33. data/app/views/spree/admin/shared/_translations.html.erb +3 -0
  34. data/app/views/spree/admin/shipping_methods/_form.html.erb +1 -1
  35. data/app/views/spree/admin/variants/_autocomplete.js.erb +2 -2
  36. data/app/views/spree/admin/variants/search.rabl +8 -10
  37. data/app/views/spree/checkout/payment/_gateway.html.erb +2 -2
  38. data/app/views/spree/order_mailer/cancel_email.text.erb +2 -2
  39. data/app/views/spree/order_mailer/confirm_email.text.erb +2 -2
  40. data/app/views/spree/orders/_line_item.html.erb +1 -1
  41. data/app/views/spree/products/_cart_form.html.erb +6 -2
  42. data/app/views/spree/shared/_google_analytics.html.erb +8 -8
  43. data/app/views/spree/shared/_order_details.html.erb +3 -1
  44. data/config/locales/en.yml +9 -7
  45. data/config/routes.rb +9 -0
  46. data/db/migrate/20121126040517_add_last_ip_to_spree_orders.rb +5 -0
  47. data/lib/spree/core/calculated_adjustments.rb +2 -1
  48. data/lib/spree/core/controller_helpers/common.rb +5 -0
  49. data/lib/spree/core/controller_helpers/order.rb +1 -0
  50. data/lib/spree/core/search/base.rb +5 -1
  51. data/lib/spree/core/testing_support/capybara_ext.rb +79 -0
  52. data/lib/spree/core/version.rb +1 -1
  53. metadata +13 -6
  54. data/vendor/assets/javascripts/jquery.tokeninput.js +0 -860
@@ -34,7 +34,9 @@ module Spree
34
34
  # Shipping methods do not have promotions attached, but promotions do
35
35
  # Therefore we must check for promotions
36
36
  if self.calculable.respond_to?(:promotion)
37
- self.calculable.promotion.rules.map(&:products).flatten
37
+ self.calculable.promotion.rules.map do |rule|
38
+ rule.respond_to?(:products) ? rule.products : []
39
+ end.flatten
38
40
  end
39
41
  end
40
42
  end
@@ -100,7 +100,7 @@ module Spree
100
100
 
101
101
  def ensure_not_shipped
102
102
  if order.try(:inventory_units).to_a.any?{ |unit| unit.variant_id == variant_id && unit.shipped? }
103
- errors.add :base, I18n.t('validation.cannot_destory_line_item_as_inventory_units_have_shipped')
103
+ errors.add :base, I18n.t('validation.cannot_destroy_line_item_as_inventory_units_have_shipped')
104
104
  return false
105
105
  end
106
106
  end
@@ -108,7 +108,7 @@ module Spree
108
108
  # Validation
109
109
  def stock_availability
110
110
  return if sufficient_stock?
111
- errors.add(:quantity, I18n.t('validation.cannot_be_greater_than_available_stock'))
111
+ errors.add(:quantity, I18n.t('validation.exceeds_available_stock'))
112
112
  end
113
113
 
114
114
  def quantity_no_less_than_shipped
@@ -126,7 +126,10 @@ module Spree
126
126
  end
127
127
  checkout_steps << step
128
128
  end
129
- checkout_steps.map(&:to_s)
129
+ steps = checkout_steps.map(&:to_s)
130
+ # Ensure there is always a complete step
131
+ steps << "complete" unless steps.include?("complete")
132
+ steps
130
133
  end
131
134
  end
132
135
  end
@@ -24,7 +24,7 @@ module Spree
24
24
  order.payment_required?
25
25
  }
26
26
  go_to_state :confirm, :if => lambda { |order| order.confirmation_required? }
27
- go_to_state :complete, :if => lambda { |order| (order.payment_required? && order.paid?) || !order.payment_required? }
27
+ go_to_state :complete, :if => lambda { |order| (order.payment_required? && order.payments.exists?) || !order.payment_required? }
28
28
  remove_transition :from => :delivery, :to => :confirm
29
29
  end
30
30
 
@@ -355,6 +355,10 @@ module Spree
355
355
  end
356
356
  end
357
357
 
358
+ def can_ship?
359
+ self.complete? || self.resumed?
360
+ end
361
+
358
362
  def credit_cards
359
363
  credit_card_ids = payments.from_credit_card.map(&:source_id).uniq
360
364
  CreditCard.scoped(:conditions => { :id => credit_card_ids })
@@ -110,7 +110,7 @@ module Spree
110
110
  def gateway_options
111
111
  options = { :email => order.email,
112
112
  :customer => order.email,
113
- :ip => '192.168.1.100', # TODO: Use an actual IP
113
+ :ip => order.last_ip_address,
114
114
  :order_id => order.number }
115
115
 
116
116
  options.merge!({ :shipping => order.ship_total * 100,
@@ -10,16 +10,13 @@ module Spree::Preferences
10
10
  # cache_key will be nil for new objects, then if we check if there
11
11
  # is a pending preference before going to default
12
12
  define_method preference_getter_method(name) do
13
- if preference_cache_key(name) && preference_store.exist?(preference_cache_key(name))
14
- preference_store.get preference_cache_key(name)
13
+
14
+ # perference_cache_key will only be nil/false for new records
15
+ #
16
+ if preference_cache_key(name)
17
+ preference_store.get(preference_cache_key(name), default)
15
18
  else
16
- if get_pending_preference(name)
17
- get_pending_preference(name)
18
- elsif Spree::Preference.table_exists? && preference = Spree::Preference.find_by_key(name.to_s)
19
- preference.value
20
- else
21
- send self.class.preference_default_getter_method(name)
22
- end
19
+ get_pending_preference(name) || default
23
20
  end
24
21
  end
25
22
  alias_method prefers_getter_method(name), preference_getter_method(name)
@@ -27,25 +27,36 @@ module Spree::Preferences
27
27
  should_persist? && Spree::Preference.where(:key => key).exists?
28
28
  end
29
29
 
30
- def get(key)
30
+ def get(key,fallback=nil)
31
31
  # return the retrieved value, if it's in the cache
32
- if (val = @cache.read(key)).present?
32
+ # use unless nil? incase the value is actually boolean false
33
+ #
34
+ unless (val = @cache.read(key)).nil?
33
35
  return val
34
36
  end
35
37
 
36
- return nil unless should_persist?
38
+ if should_persist?
39
+ # If it's not in the cache, maybe it's in the database, but
40
+ # has been cleared from the cache
37
41
 
38
- # If it's not in the cache, maybe it's in the database, but
39
- # has been cleared from the cache
42
+ # does it exist in the database?
43
+ if Spree::Preference.table_exists? && preference = Spree::Preference.find_by_key(key)
44
+ # it does exist, so let's put it back into the cache
45
+ @cache.write(preference.key, preference.value)
40
46
 
41
- # does it exist in the database?
42
- if preference = Spree::Preference.find_by_key(key)
43
- # it does exist, so let's put it back into the cache
44
- @cache.write(preference.key, preference.value)
47
+ # and return the value
48
+ return preference.value
49
+ end
50
+ end
45
51
 
46
- # and return the value
47
- preference.value
52
+ unless fallback.nil?
53
+ # cache fallback so we won't hit the db above on
54
+ # subsequent queries for the same key
55
+ #
56
+ @cache.write(key, fallback)
48
57
  end
58
+
59
+ return fallback
49
60
  end
50
61
 
51
62
  def delete(key)
@@ -189,7 +189,11 @@ module Spree
189
189
 
190
190
  # Can't use add_search_scope for this as it needs a default argument
191
191
  def self.available(available_on = nil, currency = nil)
192
- joins(:master => :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.now).where('spree_prices.currency' => currency || Spree::Config[:currency]).where('spree_prices.amount IS NOT NULL')
192
+ scope = joins(:master => :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.now)
193
+ unless Spree::Config.show_products_without_price
194
+ scope = scope.where('spree_prices.currency' => currency || Spree::Config[:currency]).where('spree_prices.amount IS NOT NULL')
195
+ end
196
+ scope
193
197
  end
194
198
  search_scopes << :available
195
199
 
@@ -28,7 +28,7 @@ module Spree
28
28
 
29
29
  scope :with_state, lambda { |s| where(:state => s) }
30
30
  scope :shipped, with_state('shipped')
31
- scope :ready, with_state('ready')
31
+ scope :ready, with_state('ready')
32
32
  scope :pending, with_state('pending')
33
33
 
34
34
  def to_param
@@ -51,11 +51,13 @@ module Spree
51
51
  def cost
52
52
  adjustment ? adjustment.amount : 0
53
53
  end
54
+
54
55
  alias_method :amount, :cost
55
56
 
56
57
  def display_cost
57
58
  Spree::Money.new(cost, { :currency => currency })
58
59
  end
60
+
59
61
  alias_method :display_amount, :display_cost
60
62
 
61
63
  # shipment state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
@@ -110,7 +112,7 @@ module Spree
110
112
  # shipped if already shipped (ie. does not change the state)
111
113
  # ready all other cases
112
114
  def determine_state(order)
113
- return 'pending' unless order.complete?
115
+ return 'pending' unless order.can_ship?
114
116
  return 'pending' if inventory_units.any? &:backordered?
115
117
  return 'shipped' if state == 'shipped'
116
118
  order.paid? ? 'ready' : 'pending'
@@ -121,7 +123,7 @@ module Spree
121
123
  return number unless number.blank?
122
124
  record = true
123
125
  while record
124
- random = "H#{Array.new(11){rand(9)}.join}"
126
+ random = "H#{Array.new(11) { rand(9) }.join}"
125
127
  record = self.class.where(:number => random).first
126
128
  end
127
129
  self.number = random
@@ -158,9 +160,10 @@ module Spree
158
160
  def ensure_correct_adjustment
159
161
  if adjustment
160
162
  adjustment.originator = shipping_method
163
+ adjustment.label = shipping_method.adjustment_label
161
164
  adjustment.save
162
165
  else
163
- shipping_method.create_adjustment(I18n.t(:shipping), order, self, true)
166
+ shipping_method.create_adjustment(shipping_method.adjustment_label, order, self, true)
164
167
  reload #ensure adjustment is present on later saves
165
168
  end
166
169
  end
@@ -15,6 +15,10 @@ module Spree
15
15
 
16
16
  calculated_adjustments
17
17
 
18
+ def adjustment_label
19
+ I18n.t(:shipping)
20
+ end
21
+
18
22
  def available?(order, display_on = nil)
19
23
  displayable?(display_on) && calculator.available?(order)
20
24
  end
@@ -0,0 +1,19 @@
1
+ <script type='text/template' id='customer_autocomplete_template'>
2
+ <div class='customer-autocomplete-item'>
3
+ <div class='customer-details'>
4
+ <h5>{{customer.email}}</h5>
5
+ {{#if bill_address.firstname }}
6
+ <strong>{{t 'bill_address' }}</strong>
7
+ {{bill_address.firstname}} {{bill_address.lastname}}<br>
8
+ {{bill_address.address1}}, {{bill_address.address2}}<br>
9
+ {{bill_address.city}}<br>
10
+ {{#if bill_address.state_id }}
11
+ {{bill_address.state.name}}
12
+ {{else}}
13
+ {{bill_address.state_name}}
14
+ {{/if}}
15
+ {{bill_address.country.name}}
16
+ {{/if}}
17
+ </div>
18
+ </div>
19
+ </script>
@@ -11,15 +11,16 @@
11
11
  <% end %>
12
12
 
13
13
  <% if @order.cart? %>
14
- <div id="add-line-item" data-hook>
14
+ <div id="select-customer" data-hook>
15
15
  <fieldset class="no-border-bottom">
16
16
  <legend align="center"><%= t(:customer_search) %></legend>
17
- <%= label_tag :customer_search, t(:enter_at_least_five_letters) %>
18
- <%= text_field_tag :customer_search, nil, :class => 'fullwidth title' %>
17
+ <%= hidden_field_tag :customer_search, nil, :class => 'fullwidth title' %>
18
+ <%= render :partial => "spree/admin/orders/customer_details/autocomplete", :formats => :js %>
19
19
  </fieldset>
20
20
  </div>
21
21
  <% end %>
22
22
 
23
+
23
24
  <%= render :partial => 'spree/shared/error_messages', :locals => { :target => @order } %>
24
25
 
25
26
  <%= form_for @order, :url => admin_order_customer_url(@order) do |f| %>
@@ -1,7 +1,7 @@
1
1
  <%= csrf_meta_tags %>
2
2
  <% content_for :page_actions do %>
3
3
  <li><%= event_links %></li>
4
- <li><%= button_link_to t(:resend), resend_admin_order_url(@order), :method => :post, :icon => 'icon-email' if @order.user %></li>
4
+ <li><%= button_link_to t(:resend), resend_admin_order_url(@order), :method => :post, :icon => 'icon-email' %></li>
5
5
  <li><%= button_link_to t(:back_to_orders_list), admin_orders_path, :icon => 'icon-arrow-left' %></li>
6
6
  <% end %>
7
7
 
@@ -0,0 +1,6 @@
1
+ collection @products
2
+ attributes :sku, :name, :id
3
+
4
+ child(:variant_images => :images) do
5
+ attributes :mini_url
6
+ end
@@ -1,32 +1,30 @@
1
- object false
2
- child @users => :users do
3
- attributes :email, :id
4
- address_fields = [:firstname, :lastname,
5
- :address1, :address2,
6
- :city, :zipcode,
7
- :phone, :state_name,
8
- :state_id, :country_id,
9
- :company]
1
+ collection(@users)
2
+ attributes :email, :id
3
+ address_fields = [:firstname, :lastname,
4
+ :address1, :address2,
5
+ :city, :zipcode,
6
+ :phone, :state_name,
7
+ :state_id, :country_id,
8
+ :company]
10
9
 
11
- child :ship_address => :ship_address do
12
- attributes *address_fields
13
- child :state do
14
- attributes :name
15
- end
10
+ child :ship_address => :ship_address do
11
+ attributes *address_fields
12
+ child :state do
13
+ attributes :name
14
+ end
16
15
 
17
- child :country do
18
- attributes :name
19
- end
16
+ child :country do
17
+ attributes :name
20
18
  end
19
+ end
21
20
 
22
- child :bill_address => :bill_address do
23
- attributes *address_fields
24
- child :state do
25
- attributes :name
26
- end
21
+ child :bill_address => :bill_address do
22
+ attributes *address_fields
23
+ child :state do
24
+ attributes :name
25
+ end
27
26
 
28
- child :country do
29
- attributes :name
30
- end
27
+ child :country do
28
+ attributes :name
31
29
  end
32
30
  end
@@ -8,7 +8,7 @@
8
8
  </title>
9
9
 
10
10
  <!-- Get "Open Sans" font from Google -->
11
- <link href='http://fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600&subset=latin,cyrillic,greek,vietnamese' rel='stylesheet' type='text/css'>
11
+ <link href='//fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600&subset=latin,cyrillic,greek,vietnamese' rel='stylesheet' type='text/css'>
12
12
 
13
13
  <%= stylesheet_link_tag 'admin/all' %>
14
14
 
@@ -1,7 +1,8 @@
1
1
  <script>
2
2
  Spree.routes = <%== {
3
3
  :variants_search => spree.admin_search_variants_path(:format => 'json'),
4
- :taxon_search => spree.search_admin_taxons_path(:format => 'json'),
5
- :user_search => spree.admin_search_users_path(:format => 'json')
4
+ :taxon_search => spree.search_admin_taxons_path(:format => 'json'),
5
+ :user_search => spree.admin_search_users_path(:format => 'json'),
6
+ :product_search => spree.search_admin_products_path(:format => 'json')
6
7
  }.to_json %>;
7
8
  </script>
@@ -6,6 +6,8 @@
6
6
  :abbr_day_names => I18n.t(:abbr_day_names, :scope => :date),
7
7
  :add => I18n.t(:add),
8
8
  :are_you_sure_delete => I18n.t(:are_you_sure_delete),
9
+ :bill_address => I18n.t(:bill_address),
10
+ :choose_a_customer => I18n.t(:choose_a_customer),
9
11
  :confirm_delete => I18n.t(:confirm_delete),
10
12
  :cut => I18n.t(:cut),
11
13
  :destroy => I18n.t(:destroy),
@@ -24,6 +26,7 @@
24
26
  :searching => I18n.t(:searching),
25
27
  :sku => I18n.t(:sku),
26
28
  :type_to_search => I18n.t(:type_to_search),
29
+ :taxon_placeholder => I18n.t(:taxon_placeholder),
27
30
  :value => I18n.t(:value)
28
31
  }.to_json
29
32
  %>
@@ -24,7 +24,7 @@
24
24
  </div>
25
25
  </div>
26
26
 
27
- <div data-hook"admin_shipping_method_form_availability_fields" class="alpha six columns">
27
+ <div data-hook="admin_shipping_method_form_availability_fields" class="alpha six columns">
28
28
  <fieldset class="categories no-border-bottom">
29
29
  <legend align="center"><%= t(:availability) %></legend>
30
30
 
@@ -13,8 +13,8 @@
13
13
  <h6 class="variant-name">{{variant.name}}</h6>
14
14
 
15
15
  <ul class='variant-data'>
16
- <li class='variant-sku'><strong>{{translations.sku}}:</strong> {{variant.sku}}</li>
17
- <li class='variant-on_hand'><strong>{{translations.on_hand}}:</strong> {{variant.count_on_hand}}</li>
16
+ <li class='variant-sku'><strong>{{t 'sku'}}:</strong> {{variant.sku}}</li>
17
+ <li class='variant-on_hand'><strong>{{t 'on_hand' }}:</strong> {{variant.count_on_hand}}</li>
18
18
  </ul>
19
19
 
20
20
  {{#if variant.option_values}}
@@ -1,15 +1,13 @@
1
- object false
2
- child(@variants => :variants) do
3
- attributes :sku, :options_text, :count_on_hand, :id, :name
1
+ collection @variants
2
+ attributes :sku, :options_text, :count_on_hand, :id, :name
4
3
 
5
- child(:images => :images) do
6
- attributes :mini_url
7
- end
4
+ child(:images => :images) do
5
+ attributes :mini_url
6
+ end
8
7
 
9
- child(:option_values => :option_values) do
10
- child(:option_type => :option_type) do
11
- attributes :name, :presentation
12
- end
8
+ child(:option_values => :option_values) do
9
+ child(:option_type => :option_type) do
13
10
  attributes :name, :presentation
14
11
  end
12
+ attributes :name, :presentation
15
13
  end
@@ -15,8 +15,8 @@
15
15
  </p>
16
16
  <p class="field" data-hook="card_expiration">
17
17
  <%= label_tag nil, t(:expiration) %><br />
18
- <%= select_month(Date.today, :prefix => param_prefix, :field_name => 'month', :use_month_numbers => true, :class => 'required') %>
19
- <%= select_year(Date.today, :prefix => param_prefix, :field_name => 'year', :start_year => Date.today.year, :end_year => Date.today.year + 15, :class => 'required') %>
18
+ <%= select_month(Date.today, { :prefix => param_prefix, :field_name => 'month', :use_month_numbers => true }, :class => 'required') %>
19
+ <%= select_year(Date.today, { :prefix => param_prefix, :field_name => 'year', :start_year => Date.today.year, :end_year => Date.today.year + 15 }, :class => 'required') %>
20
20
  <span class="required">*</span>
21
21
  </p>
22
22
  <p class="field" data-hook="card_code">
@@ -9,8 +9,8 @@
9
9
  <%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= item.variant.display_amount %> = <%= item.display_amount %>
10
10
  <% end %>
11
11
  ============================================================
12
- <%= t('order_mailer.cancel_email.subtotal') %> <%= @order.display_item_total %>
12
+ <%= t('order_mailer.cancel_email.subtotal', :subtotal => @order.display_item_total) %>
13
13
  <% @order.adjustments.eligible.each do |adjustment| %>
14
14
  <%= raw(adjustment.label) %> <%= adjustment.display_amount %>
15
15
  <% end %>
16
- <%= t('order_mailer.cancel_email.total') %> <%= @order.display_total %>
16
+ <%= t('order_mailer.cancel_email.total', :total => @order.display_total) %>