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
@@ -1,4 +1,5 @@
1
1
  //= require_self
2
+ //= require admin/handlebar_extensions
2
3
  //= require admin/variant_autocomplete
3
4
  //= require admin/taxon_autocomplete
4
5
  //= require admin/spree-select2
@@ -157,6 +158,13 @@ $(document).ready(function(){
157
158
  el.attr("id", el.attr("id").replace(/\d+/, new_id))
158
159
  el.attr("name", el.attr("name").replace(/\d+/, new_id))
159
160
  })
161
+ // When cloning a new row, set the href of all icons to be an empty "#"
162
+ // This is so that clicking on them does not perform the actions for the
163
+ // duplicated row
164
+ new_table_row.find("a").each(function () {
165
+ var el = $(this);
166
+ el.attr('href', '#');
167
+ })
160
168
  $(target).prepend(new_table_row);
161
169
  })
162
170
 
@@ -183,8 +191,26 @@ $(document).ready(function(){
183
191
  });
184
192
 
185
193
  $('body').on('click', 'a.remove_fields', function() {
186
- $(this).prev("input[type=hidden]").val("1");
187
- $(this).closest(".fields").hide();
194
+ el = $(this);
195
+ el.prev("input[type=hidden]").val("1");
196
+ el.closest(".fields").hide();
197
+ if (el.attr("href")) {
198
+ $.ajax({
199
+ type: 'POST',
200
+ url: el.attr("href"),
201
+ data: {
202
+ _method: 'delete',
203
+ authenticity_token: AUTH_TOKEN
204
+ },
205
+ success: function(response) {
206
+ el.parents("tr").fadeOut('hide');
207
+ },
208
+ error: function(response, textStatus, errorThrown) {
209
+ show_flash_error(response.responseText);
210
+ }
211
+
212
+ })
213
+ }
188
214
  return false;
189
215
  });
190
216
 
@@ -1,108 +1,68 @@
1
- $(document).ready(function(){
2
-
3
- add_address = function(addr){
4
- var html = "";
5
- if(addr!=undefined){
6
- html += addr['firstname'] + " " + addr['lastname'] + ", ";
7
- html += addr['address1'] + ", " + addr['address2'] + ", ";
8
- html += addr['city'] + ", ";
9
-
10
- if(addr['state_id']!=null){
11
- html += addr['state']['name'] + ", ";
12
- }else{
13
- html += addr['state_name'] + ", ";
14
- }
15
-
16
- html += addr['country']['name'];
17
- }
18
- return html;
19
- }
20
-
21
- format_user_autocomplete = function(item){
22
- var data = item.data
23
- var html = "<h4>" + data['email'] +"</h4>";
24
- html += "<span><strong>Billing:</strong> ";
25
- html += add_address(data['bill_address']);
26
- html += "</span>";
27
-
28
- html += "<span><strong>Shipping:</strong> ";
29
- html += add_address(data['ship_address']);
30
- html += "</span>";
31
-
32
- return html
33
- }
34
-
35
- prep_user_autocomplete_data = function(data){
36
- return $.map(eval(data['users']), function(row) {
37
- return {
38
- data: row,
39
- value: row['email'],
40
- result: row['email']
41
- }
42
- });
1
+ $(document).ready(function() {
2
+ window.customerTemplate = Handlebars.compile($('#customer_autocomplete_template').text());
3
+
4
+ formatCustomerResult = function(customer) {
5
+ return customerTemplate({
6
+ customer: customer,
7
+ bill_address: customer.bill_address,
8
+ ship_address: customer.ship_address
9
+ })
43
10
  }
44
11
 
45
12
  if ($("#customer_search").length > 0) {
46
- $("#customer_search").autocomplete({
47
- minChars: 5,
48
- delay: 500,
49
- source: function(request, response) {
50
- var params = { q: $('#customer_search').val(),
51
- authenticity_token: AUTH_TOKEN }
52
- $.get(Spree.routes.user_search + '&' + jQuery.param(params), function(data) {
53
- result = prep_user_autocomplete_data(data)
54
- response(result);
55
- });
56
- },
57
- focus: function(event, ui) {
58
- $('#customer_search').val(ui.item.label);
59
- $(ui).addClass('ac_over');
60
- return false;
13
+ $("#customer_search").select2({
14
+ placeholder: Spree.translations.choose_a_customer,
15
+ ajax: {
16
+ url: Spree.routes.user_search,
17
+ datatype: 'json',
18
+ data: function(term, page) {
19
+ return { q: term }
20
+ },
21
+ results: function(data, page) {
22
+ return { results: data }
23
+ }
61
24
  },
62
- select: function(event, ui) {
63
- $('#customer_search').val(ui.item.label);
64
- _.each(['bill', 'ship'], function(addr_name){
65
- var addr = ui.item.data[addr_name + '_address'];
66
- if(addr!=undefined){
67
- $('#order_' + addr_name + '_address_attributes_firstname').val(addr['firstname']);
68
- $('#order_' + addr_name + '_address_attributes_lastname').val(addr['lastname']);
69
- $('#order_' + addr_name + '_address_attributes_company').val(addr['company']);
70
- $('#order_' + addr_name + '_address_attributes_address1').val(addr['address1']);
71
- $('#order_' + addr_name + '_address_attributes_address2').val(addr['address2']);
72
- $('#order_' + addr_name + '_address_attributes_city').val(addr['city']);
73
- $('#order_' + addr_name + '_address_attributes_zipcode').val(addr['zipcode']);
74
- $('#order_' + addr_name + '_address_attributes_state_id').val(addr['state_id']);
75
- $('#order_' + addr_name + '_address_attributes_country_id').val(addr['country_id']);
76
- $('#order_' + addr_name + '_address_attributes_phone').val(addr['phone']);
25
+ formatResult: formatCustomerResult,
26
+ formatSelection: function (customer) {
27
+ _.each(['bill_address', 'ship_address'], function(address) {
28
+ var data = customer[address];
29
+ address_parts = ['firstname', 'lastname',
30
+ 'company', 'address1',
31
+ 'address2', 'city',
32
+ 'zipcode', 'phone']
33
+ var attribute_wrapper = '#order_' + address + '_attributes_'
34
+ if(data != undefined) {
35
+ _.each(address_parts, function(part) {
36
+ $(attribute_wrapper + part).val(data[part]);
37
+ })
38
+
39
+ $(attribute_wrapper + 'state_id').select2("val", data['state_id']);
40
+ $(attribute_wrapper + 'country_id').select2("val", data['country_id']);
41
+ }
42
+ else {
43
+ _.each(address_parts, function(part) {
44
+ $(attribute_wrapper + part).val("");
45
+ })
46
+
47
+ $(attribute_wrapper + 'state_id').select2("val", '');
48
+ $(attribute_wrapper + 'country_id').select2("val", '');
77
49
  }
78
50
  });
79
51
 
80
- $('#order_email').val(ui.item.data['email']);
81
- $('#user_id').val(ui.item.data['id']);
52
+ $('#order_email').val(customer.email);
53
+ $('#user_id').val(customer.id);
82
54
  $('#guest_checkout_true').prop("checked", false);
83
55
  $('#guest_checkout_false').prop("checked", true);
84
56
  $('#guest_checkout_false').prop("disabled", false);
85
- return true;
86
- }
87
- }).data("autocomplete")._renderItem = function(ul, item) {
88
- $(ul).addClass('ac_results');
89
- html = format_user_autocomplete(item);
90
- return $("<li></li>")
91
- .data("item.autocomplete", item)
92
- .append("<a class='ui-menu-item'>" + html + "</a>")
93
- .appendTo(ul);
94
- }
95
-
96
- $("#customer_search").data("autocomplete")._resizeMenu = function() {
97
- var ul = this.menu.element;
98
- ul.outerWidth(this.element.outerWidth());
99
- }
100
-
101
57
 
58
+ return customer.email;
59
+ }
60
+ })
102
61
  }
103
62
 
104
- var show_billing = function(show) {
105
- if(show) {
63
+
64
+ $('input#order_use_billing').click(function() {
65
+ if(!$(this).is(':checked')) {
106
66
  $('#shipping').show();
107
67
  $('#shipping input').prop("disabled", false);
108
68
  $('#shipping select').prop("disabled", false);
@@ -111,10 +71,6 @@ $(document).ready(function(){
111
71
  $('#shipping input').prop("disabled", true);
112
72
  $('#shipping select').prop("disabled", true);
113
73
  }
114
- }
115
-
116
- $('input#order_use_billing').click(function() {
117
- show_billing(!$(this).is(':checked'));
118
74
  });
119
75
 
120
76
  $('#guest_checkout_true').change(function() {
@@ -0,0 +1,9 @@
1
+ //= require handlebars
2
+ Handlebars.registerHelper("t", function(key) {
3
+ if (Spree.translations[key]) {
4
+ return Spree.translations[key]
5
+ } else {
6
+ console.error("No translation found for " + key + ". Does it exist within spree/admin/shared/_translations.html.erb?")
7
+ }
8
+ });
9
+
@@ -1,24 +1,22 @@
1
1
  //= require select2
2
2
  jQuery(function($) {
3
- <% unless Rails.env.test? %>
4
- // Make select beautiful
5
- $('select.select2').select2({
6
- allowClear: true
7
- });
3
+ // Make select beautiful
4
+ $('select.select2').select2({
5
+ allowClear: true
6
+ });
8
7
 
9
- function format_taxons(taxon) {
10
- new_taxon = taxon.text.replace('->', '<i class="icon-arrow-right">')
11
- return new_taxon;
12
- }
8
+ function format_taxons(taxon) {
9
+ new_taxon = taxon.text.replace('->', '<i class="icon-arrow-right">')
10
+ return new_taxon;
11
+ }
13
12
 
14
- $("#product_taxon_ids").on({
15
- change: function(e){
16
- $('.select2-search-choice .with-tip').powerTip({
17
- smartPlacement: true,
18
- fadeInTime: 50,
19
- fadeOutTime: 50
20
- })
21
- }
22
- })
23
- <% end %>
13
+ $("#product_taxon_ids").on({
14
+ change: function(e){
15
+ $('.select2-search-choice .with-tip').powerTip({
16
+ smartPlacement: true,
17
+ fadeInTime: 50,
18
+ fadeOutTime: 50
19
+ })
20
+ }
21
+ })
24
22
  })
@@ -1,7 +1,6 @@
1
1
  //= require jquery-ui
2
2
  //= require modernizr
3
3
  //= require jquery.cookie
4
- //= require jquery.tokeninput
5
4
  //= require jquery.delayedobserver
6
5
  //= require jquery.jstree/jquery.jstree
7
6
  //= require jquery.alerts/jquery.alerts
@@ -0,0 +1,9 @@
1
+ $(document).ready(function() {
2
+ $("#country").change(function() {
3
+ var new_state_link_href = $('#new_state_link a').attr('href');
4
+ var selected_country_id = $('#country option:selected').attr('value');
5
+ var new_link = new_state_link_href.replace(/countries\/(\d+)/,
6
+ 'countries/'+selected_country_id);
7
+ $('#new_state_link a').attr('href', new_link);
8
+ });
9
+ });
@@ -8,7 +8,7 @@ function cleanTaxons(data) {
8
8
  $(document).ready(function() {
9
9
  if ($("#product_taxon_ids").length > 0) {
10
10
  $("#product_taxon_ids").select2({
11
- placeholder: "Add a taxon",
11
+ placeholder: Spree.translations.taxon_placeholder,
12
12
  multiple: true,
13
13
  initSelection: function(element, callback) {
14
14
  return $.getJSON(Spree.routes.taxon_search + "?ids=" + (element.val()), null, function(data) {
@@ -1,4 +1,3 @@
1
- //= require handlebars
2
1
  <%#encoding: UTF-8%>
3
2
  // variant autocompletion
4
3
 
@@ -10,7 +9,7 @@ formatVariantResult = function(variant) {
10
9
  if (variant["images"][0] != undefined) {
11
10
  variant.image = variant.images[0].image.mini_url
12
11
  }
13
- return variantTemplate({ variant: variant, translations: Spree.translations })
12
+ return variantTemplate({ variant: variant })
14
13
  }
15
14
 
16
15
  $.fn.variantAutocomplete = function() {
@@ -25,10 +24,7 @@ $.fn.variantAutocomplete = function() {
25
24
  return { q: term }
26
25
  },
27
26
  results: function (data, page) {
28
- var variants = $.map(data['variants'], function(result) {
29
- return result['variant']
30
- })
31
- return { results: variants }
27
+ return { results: data }
32
28
  }
33
29
  },
34
30
  formatResult: formatVariantResult,
@@ -69,11 +69,11 @@ $ ->
69
69
  ).triggerHandler 'click'
70
70
 
71
71
  if ($ '#checkout_form_payment').is('*')
72
- # Activate already checked payment method if form is re-rendered
73
- # i.e. if user enters invalid data
74
- ($ 'input[type="radio"]:checked').click()
75
-
76
72
  ($ 'input[type="radio"][name="order[payments_attributes][][payment_method_id]"]').click(->
77
73
  ($ '#payment-methods li').hide()
78
74
  ($ '#payment_method_' + @value).show() if @checked
79
75
  )
76
+
77
+ # Activate already checked payment method if form is re-rendered
78
+ # i.e. if user enters invalid data
79
+ ($ 'input[type="radio"]:checked').click()
@@ -610,6 +610,14 @@ mark {background-color: $link_text_color; color: $layout_background_color; font-
610
610
  /*--------------------------------------*/
611
611
  /* Checkout
612
612
  /*--------------------------------------*/
613
+ .out-of-stock {
614
+ background: #df0000;
615
+ color: white;
616
+ padding: 5px;
617
+ padding-right: 10px;
618
+ font-weight: bold;
619
+ }
620
+
613
621
  .progress-steps {
614
622
  list-style: decimal inside;
615
623
  overflow: auto;
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module Admin
3
+ class OptionValuesController < Spree::Admin::BaseController
4
+ def destroy
5
+ option_value = Spree::OptionValue.find(params[:id])
6
+ option_value.destroy
7
+ render :text => nil
8
+ end
9
+ end
10
+ end
11
+ end
@@ -5,6 +5,18 @@ module Spree
5
5
  before_filter :find_properties
6
6
  before_filter :setup_property, :only => [:index]
7
7
 
8
+ # We use a "custom" finder in destroy
9
+ # Because the request is scoped without a product
10
+ # on account of the request coming from the "link_to_remove_fields"
11
+ # helper on the admin/product_properties view
12
+ skip_before_filter :load_resource, :only => [:destroy]
13
+
14
+ def destroy
15
+ product_property = Spree::ProductProperty.find(params[:id])
16
+ product_property.destroy
17
+ render :text => nil
18
+ end
19
+
8
20
  private
9
21
  def find_properties
10
22
  @properties = Spree::Property.pluck(:name)
@@ -16,6 +16,15 @@ module Spree
16
16
  respond_with(@collection)
17
17
  end
18
18
 
19
+ def search
20
+ if params[:ids]
21
+ @products = Spree::Product.where(:id => params[:ids].split(","))
22
+ else
23
+ search_params = { :name_cont => params[:q], :sku_cont => params[:q] }
24
+ @products = Spree::Product.ransack(search_params.merge(:m => 'or')).result
25
+ end
26
+ end
27
+
19
28
  def update
20
29
  if params[:product][:taxon_ids].present?
21
30
  params[:product][:taxon_ids] = params[:product][:taxon_ids].split(',')
@@ -132,13 +132,6 @@ module Spree
132
132
  }.join("<br />").html_safe
133
133
  end
134
134
 
135
- def product_picker_field(name, value)
136
- products = Product.with_ids(value.split(','))
137
- product_names = products.inject({}){|memo,item| memo[item.id] = item.name; memo}
138
- product_rules = products.collect{ |p| { :id => p.id, :name => p.name } }
139
- %(<input type="text" name="#{name}" value="#{value}" class="tokeninput products" data-names='#{product_names.to_json}' data-pre='#{product_rules.to_json}'/>).html_safe
140
- end
141
-
142
135
  def link_to_add_fields(name, target, options = {})
143
136
  name = '' if options[:no_text]
144
137
  css_classes = options[:class] ? options[:class] + " add_fields" : "add_fields"
@@ -150,7 +143,8 @@ module Spree
150
143
  name = '' if options[:no_text]
151
144
  options[:class] = '' unless options[:class]
152
145
  options[:class] += 'no-text with-tip' if options[:no_text]
153
- link_to_with_icon('icon-trash', name, '#', :class => "remove_fields #{options[:class]}", :data => {:action => 'remove'}, :title => t(:remove)) + f.hidden_field(:_destroy)
146
+ url = f.object.persisted? ? [:admin, f.object] : '#'
147
+ link_to_with_icon('icon-trash', name, url, :class => "remove_fields #{options[:class]}", :data => {:action => 'remove'}, :title => t(:remove)) + f.hidden_field(:_destroy)
154
148
  end
155
149
 
156
150
  def spree_dom_id(record)
@@ -61,6 +61,7 @@ module Spree
61
61
  preference :show_only_complete_orders_by_default, :boolean, :default => true
62
62
  preference :show_zero_stock_products, :boolean, :default => true
63
63
  preference :show_variant_full_price, :boolean, :default => false #Displays variant full price or difference with product price. Default false to be compatible with older behavior
64
+ preference :show_products_without_price, :boolean, :default => false
64
65
  preference :site_name, :string, :default => 'Spree Demo Site'
65
66
  preference :site_url, :string, :default => 'demo.spreecommerce.com'
66
67
  preference :tax_using_ship_address, :boolean, :default => true