spree_core 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/app/assets/javascripts/admin/admin.js.erb +27 -2
  2. data/app/assets/javascripts/admin/taxon_autocomplete.js.erb +25 -23
  3. data/app/assets/stylesheets/store/screen.css.scss +13 -0
  4. data/app/controllers/spree/admin/images_controller.rb +0 -11
  5. data/app/controllers/spree/admin/option_types_controller.rb +3 -14
  6. data/app/controllers/spree/admin/option_values_controller.rb +11 -0
  7. data/app/controllers/spree/admin/product_properties_controller.rb +12 -0
  8. data/app/controllers/spree/admin/resource_controller.rb +10 -0
  9. data/app/controllers/spree/admin/taxons_controller.rb +1 -1
  10. data/app/controllers/spree/admin/variants_controller.rb +0 -11
  11. data/app/controllers/spree/products_controller.rb +9 -3
  12. data/app/helpers/spree/admin/base_helper.rb +2 -1
  13. data/app/helpers/spree/admin/products_helper.rb +1 -1
  14. data/app/helpers/spree/checkout_helper.rb +15 -0
  15. data/app/models/spree/calculator/per_item.rb +3 -1
  16. data/app/models/spree/image.rb +1 -1
  17. data/app/models/spree/line_item.rb +1 -1
  18. data/app/models/spree/order.rb +25 -4
  19. data/app/models/spree/order/checkout.rb +4 -1
  20. data/app/models/spree/payment.rb +1 -1
  21. data/app/models/spree/payment/processing.rb +1 -4
  22. data/app/models/spree/preferences/preferable_class_methods.rb +6 -9
  23. data/app/models/spree/preferences/store.rb +22 -11
  24. data/app/models/spree/product.rb +1 -1
  25. data/app/models/spree/shipment.rb +19 -15
  26. data/app/models/spree/taxonomy.rb +2 -0
  27. data/app/views/spree/admin/image_settings/edit.html.erb +7 -0
  28. data/app/views/spree/admin/orders/edit.html.erb +1 -1
  29. data/app/views/spree/admin/products/_form.html.erb +6 -1
  30. data/app/views/spree/admin/shared/_order_tabs.html.erb +1 -1
  31. data/app/views/spree/admin/shared/_translations.html.erb +1 -1
  32. data/app/views/spree/admin/shipping_methods/_form.html.erb +1 -1
  33. data/app/views/spree/admin/taxonomies/_list.html.erb +2 -1
  34. data/app/views/spree/checkout/_address.html.erb +47 -47
  35. data/app/views/spree/checkout/payment/_gateway.html.erb +2 -2
  36. data/app/views/spree/layouts/spree_application.html.erb +1 -1
  37. data/app/views/spree/orders/_line_item.html.erb +1 -1
  38. data/app/views/spree/products/_thumbnails.html.erb +17 -15
  39. data/app/views/spree/products/show.html.erb +1 -1
  40. data/app/views/spree/shared/_footer.html.erb +1 -1
  41. data/app/views/spree/shared/_google_analytics.html.erb +8 -8
  42. data/app/views/spree/shared/_header.html.erb +1 -1
  43. data/app/views/spree/shared/_main_nav_bar.html.erb +1 -1
  44. data/app/views/spree/shared/_nav_bar.html.erb +1 -1
  45. data/app/views/spree/shared/_order_details.html.erb +3 -1
  46. data/app/views/spree/shared/_sidebar.html.erb +1 -1
  47. data/app/views/spree/taxons/show.html.erb +1 -1
  48. data/config/locales/en.yml +6 -15
  49. data/config/routes.rb +7 -0
  50. data/db/migrate/20121124203911_add_position_to_taxonomies.rb +5 -0
  51. data/lib/spree/core/controller_helpers.rb +1 -0
  52. data/lib/spree/core/testing_support/factories/payment_factory.rb +0 -18
  53. data/lib/spree/core/validators/email.rb +1 -1
  54. data/lib/spree/core/version.rb +1 -1
  55. data/lib/tasks/taxon.rake +1 -1
  56. metadata +147 -54
@@ -104,6 +104,13 @@ $(document).ready(function(){
104
104
  el.attr("id", el.attr("id").replace(/\d+/, new_id))
105
105
  el.attr("name", el.attr("name").replace(/\d+/, new_id))
106
106
  })
107
+ // When cloning a new row, set the href of all icons to be an empty "#"
108
+ // This is so that clicking on them does not perform the actions for the
109
+ // duplicated row
110
+ new_table_row.find("a").each(function () {
111
+ var el = $(this);
112
+ el.attr('href', '#');
113
+ })
107
114
  $(target).append(new_table_row);
108
115
  })
109
116
 
@@ -130,8 +137,26 @@ $(document).ready(function(){
130
137
  });
131
138
 
132
139
  $('body').on('click', 'a.remove_fields', function() {
133
- $(this).prev("input[type=hidden]").val("1");
134
- $(this).closest(".fields").hide();
140
+ el = $(this);
141
+ el.prev("input[type=hidden]").val("1");
142
+ el.closest(".fields").hide();
143
+ if (el.attr("href")) {
144
+ $.ajax({
145
+ type: 'POST',
146
+ url: el.attr("href"),
147
+ data: {
148
+ _method: 'delete',
149
+ authenticity_token: AUTH_TOKEN
150
+ },
151
+ success: function(response) {
152
+ el.parents("tr").fadeOut('hide');
153
+ },
154
+ error: function(response, textStatus, errorThrown) {
155
+ show_flash_error(response.responseText);
156
+ }
157
+
158
+ })
159
+ }
135
160
  return false;
136
161
  });
137
162
 
@@ -6,29 +6,31 @@ function cleanTaxons(data) {
6
6
  }
7
7
 
8
8
  $(document).ready(function() {
9
- $("#product_taxon_ids").select2({
10
- placeholder: "Add a taxon",
11
- multiple: true,
12
- initSelection: function(element, callback) {
13
- return $.getJSON(Spree.routes.taxon_search + "?ids=" + (element.val()), null, function(data) {
14
- return callback(self.cleanTaxons(data));
15
- })
16
- },
17
- ajax: {
18
- url: Spree.routes.taxon_search,
19
- datatype: 'json',
20
- data: function(term, page) {
21
- return { q: term }
9
+ if ($("#product_taxon_ids").length > 0) {
10
+ $("#product_taxon_ids").select2({
11
+ placeholder: "Add a taxon",
12
+ multiple: true,
13
+ initSelection: function(element, callback) {
14
+ return $.getJSON(Spree.routes.taxon_search + "?ids=" + (element.val()), null, function(data) {
15
+ return callback(self.cleanTaxons(data));
16
+ })
22
17
  },
23
- results: function (data, page) {
24
- return { results: self.cleanTaxons(data) }
18
+ ajax: {
19
+ url: Spree.routes.taxon_search,
20
+ datatype: 'json',
21
+ data: function(term, page) {
22
+ return { q: term }
23
+ },
24
+ results: function (data, page) {
25
+ return { results: self.cleanTaxons(data) }
26
+ }
27
+ },
28
+ formatResult: function(taxon) {
29
+ return taxon.pretty_name
30
+ },
31
+ formatSelection: function(taxon) {
32
+ return taxon.pretty_name
25
33
  }
26
- },
27
- formatResult: function(taxon) {
28
- return taxon.pretty_name
29
- },
30
- formatSelection: function(taxon) {
31
- return taxon.pretty_name
32
- }
33
- })
34
+ })
35
+ }
34
36
  })
@@ -479,6 +479,10 @@ mark {background-color: $link_text_color; color: $layout_background_color; font-
479
479
  border-color: $link_text_color;
480
480
  }
481
481
 
482
+ img {
483
+ max-width: 100%; /* Fluid images for product */
484
+ }
485
+
482
486
  }
483
487
 
484
488
  .price {
@@ -533,6 +537,7 @@ mark {background-color: $link_text_color; color: $layout_background_color; font-
533
537
 
534
538
  img {
535
539
  min-height: 240px;
540
+ max-width: 100%; /* Fluid images for product */
536
541
  }
537
542
  }
538
543
  #product-thumbnails {
@@ -600,6 +605,14 @@ mark {background-color: $link_text_color; color: $layout_background_color; font-
600
605
  /*--------------------------------------*/
601
606
  /* Checkout
602
607
  /*--------------------------------------*/
608
+ .out-of-stock {
609
+ background: #df0000;
610
+ color: white;
611
+ padding: 5px;
612
+ padding-right: 10px;
613
+ font-weight: bold;
614
+ }
615
+
603
616
  .progress-steps {
604
617
  list-style: decimal inside;
605
618
  overflow: auto;
@@ -7,17 +7,6 @@ module Spree
7
7
  update.before :set_viewable
8
8
  destroy.before :destroy_before
9
9
 
10
- def update_positions
11
- params[:positions].each do |id, index|
12
- Image.where(:id => id).update_all(:position => index)
13
- end
14
-
15
- respond_to do |format|
16
- format.js { render :text => 'Ok' }
17
- end
18
- end
19
-
20
-
21
10
  private
22
11
 
23
12
  def location_after_save
@@ -3,17 +3,6 @@ module Spree
3
3
  class OptionTypesController < ResourceController
4
4
  before_filter :setup_new_option_value, :only => [:edit]
5
5
 
6
- def update_positions
7
- params[:positions].each do |id, index|
8
- OptionType.where(:id => id).update_all(:position => index)
9
- end
10
-
11
- respond_to do |format|
12
- format.html { redirect_to admin_product_variants_url(params[:product_id]) }
13
- format.js { render :text => 'Ok' }
14
- end
15
- end
16
-
17
6
  def update_values_positions
18
7
  params[:positions].each do |id, index|
19
8
  OptionValue.where(:id => id).update_all(:position => index)
@@ -26,7 +15,7 @@ module Spree
26
15
  end
27
16
 
28
17
  protected
29
-
18
+
30
19
  def location_after_save
31
20
  if @option_type.created_at == @option_type.updated_at
32
21
  edit_admin_option_type_url(@option_type)
@@ -44,8 +33,8 @@ module Spree
44
33
  def setup_new_option_value
45
34
  @option_type.option_values.build if @option_type.option_values.empty?
46
35
  end
47
-
48
- def set_available_option_types
36
+
37
+ def set_available_option_types
49
38
  @available_option_types = if @product.option_type_ids.any?
50
39
  OptionType.where('id NOT IN (?)', @product.option_type_ids)
51
40
  else
@@ -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)
@@ -53,6 +53,16 @@ class Spree::Admin::ResourceController < Spree::Admin::BaseController
53
53
  end
54
54
  end
55
55
 
56
+ def update_positions
57
+ params[:positions].each do |id, index|
58
+ model_class.where(:id => id).update_all(:position => index)
59
+ end
60
+
61
+ respond_to do |format|
62
+ format.js { render :text => 'Ok' }
63
+ end
64
+ end
65
+
56
66
  def destroy
57
67
  invoke_callbacks(:destroy, :before)
58
68
  if @object.destroy
@@ -63,7 +63,7 @@ module Spree
63
63
  elsif new_position < new_siblings.index(@taxon)
64
64
  @taxon.move_to_left_of(new_siblings[new_position]) # we move up
65
65
  else
66
- @taxon.move_to_right_of(new_siblings[new_position]) # we move down
66
+ @taxon.move_to_right_of(new_siblings[new_position-1]) # we move down
67
67
  end
68
68
  # Reset legacy position, if any extensions still rely on it
69
69
  new_parent.children.reload.each{|t| t.update_column(:position, t.position)}
@@ -30,17 +30,6 @@ module Spree
30
30
  end
31
31
  end
32
32
 
33
- def update_positions
34
- params[:positions].each do |id, index|
35
- Variant.where(:id => id).update_all(:position => index)
36
- end
37
-
38
- respond_with(@variant) do |format|
39
- format.html { redirect_to admin_product_variants_url(params[:product_id]) }
40
- format.js { render :text => 'Ok' }
41
- end
42
- end
43
-
44
33
  protected
45
34
 
46
35
  def create_before
@@ -21,9 +21,15 @@ module Spree
21
21
 
22
22
  referer = request.env['HTTP_REFERER']
23
23
  if referer
24
- referer_path = URI.parse(request.env['HTTP_REFERER']).path
25
- if referer_path && referer_path.match(/\/t\/(.*)/)
26
- @taxon = Taxon.find_by_permalink($1)
24
+ begin
25
+ referer_path = URI.parse(request.env['HTTP_REFERER']).path
26
+ # Fix for #2249
27
+ rescue URI::InvalidURIError
28
+ # Do nothing
29
+ else
30
+ if referer_path && referer_path.match(/\/t\/(.*)/)
31
+ @taxon = Taxon.find_by_permalink($1)
32
+ end
27
33
  end
28
34
  end
29
35
 
@@ -148,7 +148,8 @@ module Spree
148
148
 
149
149
  # renders hidden field and link to remove record using nested_attributes
150
150
  def link_to_remove_fields(name, f)
151
- f.hidden_field(:_destroy) + link_to_with_icon(:delete, name, '#', :class => 'remove_fields')
151
+ url = f.object.persisted? ? [:admin, f.object] : '#'
152
+ f.hidden_field(:_destroy) + link_to_with_icon(:delete, name, url, :class => 'remove_fields')
152
153
  end
153
154
 
154
155
  def spree_dom_id(record)
@@ -18,7 +18,7 @@ module Spree
18
18
  content_tag(:option,
19
19
  :value => option_type.id,
20
20
  :selected => ('selected' if selected)) do
21
- option_type.presentation
21
+ option_type.name
22
22
  end
23
23
  end.join("").html_safe
24
24
  end
@@ -4,6 +4,16 @@ module Spree
4
4
  @order.checkout_steps
5
5
  end
6
6
 
7
+ def state_required_class
8
+ 'required' if state_required?
9
+ end
10
+
11
+ def state_required_label
12
+ if state_required?
13
+ content_tag :span, '*', :class => state_required_class
14
+ end
15
+ end
16
+
7
17
  def checkout_progress
8
18
  states = checkout_states
9
19
  items = states.map do |state|
@@ -27,5 +37,10 @@ module Spree
27
37
  end
28
38
  content_tag('ol', raw(items.join("\n")), :class => 'progress-steps', :id => "checkout-step-#{@order.state}")
29
39
  end
40
+
41
+ private
42
+ def state_required?
43
+ Spree::Config[:address_requires_state]
44
+ end
30
45
  end
31
46
  end
@@ -33,7 +33,9 @@ module Spree
33
33
  # Shipping methods do not have promotions attached, but promotions do
34
34
  # Therefore we must check for promotions
35
35
  if self.calculable.respond_to?(:promotion)
36
- self.calculable.promotion.rules.map(&:products).flatten
36
+ self.calculable.promotion.rules.map do |rule|
37
+ rule.respond_to?(:products) ? rule.products : []
38
+ end.flatten
37
39
  end
38
40
  end
39
41
  end
@@ -10,7 +10,7 @@ module Spree
10
10
  :default_style => :product,
11
11
  :url => '/spree/products/:id/:style/:basename.:extension',
12
12
  :path => ':rails_root/public/spree/products/:id/:style/:basename.:extension',
13
- :convert_options => { :all => '-strip' }
13
+ :convert_options => { :all => '-strip -auto-orient' }
14
14
  # save the w,h of the original image (from which others can be calculated)
15
15
  # we need to look at the write-queue for images which have not been saved yet
16
16
  after_post_process :find_dimensions
@@ -94,7 +94,7 @@ module Spree
94
94
  # Validation
95
95
  def stock_availability
96
96
  return if sufficient_stock?
97
- errors.add(:quantity, I18n.t('validation.cannot_be_greater_than_available_stock'))
97
+ errors.add(:quantity, I18n.t('validation.exceeds_available_stock'))
98
98
  end
99
99
 
100
100
  def quantity_no_less_than_shipped
@@ -15,9 +15,16 @@ module Spree
15
15
  checkout_flow do
16
16
  go_to_state :address
17
17
  go_to_state :delivery
18
- go_to_state :payment, :if => lambda { |order| order.payment_required? }
18
+ go_to_state :payment, :if => lambda { |order|
19
+ # Fix for #2191
20
+ if order.shipping_method
21
+ order.create_shipment!
22
+ order.send(:update_totals)
23
+ end
24
+ order.payment_required?
25
+ }
19
26
  go_to_state :confirm, :if => lambda { |order| order.confirmation_required? }
20
- go_to_state :complete
27
+ go_to_state :complete, :if => lambda { |order| (order.payment_required? && order.payments.exists?) || !order.payment_required? }
21
28
  remove_transition :from => :delivery, :to => :confirm
22
29
  end
23
30
 
@@ -63,7 +70,8 @@ module Spree
63
70
  before_create :link_by_email
64
71
  after_create :create_tax_charge!
65
72
 
66
- validates :email, :presence => true, :email => true, :if => :require_email
73
+ validates :email, :presence => true, :if => :require_email
74
+ validates :email, :email => true, :if => :require_email, :allow_blank => true
67
75
  validate :has_available_shipment
68
76
  validate :has_available_payment
69
77
 
@@ -344,6 +352,10 @@ module Spree
344
352
  end
345
353
  end
346
354
 
355
+ def can_ship?
356
+ self.complete? || self.resumed?
357
+ end
358
+
347
359
  def credit_cards
348
360
  credit_card_ids = payments.from_credit_card.map(&:source_id).uniq
349
361
  CreditCard.scoped(:conditions => { :id => credit_card_ids })
@@ -477,8 +489,17 @@ module Spree
477
489
 
478
490
  def merge!(order)
479
491
  order.line_items.each do |line_item|
480
- self.add_variant(line_item.variant, line_item.quantity)
492
+ current_line_item = self.line_items.find_by_variant_id(line_item.variant_id)
493
+ if current_line_item
494
+ current_line_item.quantity += line_item.quantity
495
+ current_line_item.save
496
+ else
497
+ line_item.order_id = self.id
498
+ line_item.save
499
+ end
481
500
  end
501
+ # So that the destroy doesn't take out line items which may have been re-assigned
502
+ order.line_items.reload
482
503
  order.destroy
483
504
  end
484
505
 
@@ -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
@@ -32,7 +32,7 @@ module Spree
32
32
  end
33
33
  # When processing during checkout fails
34
34
  event :failure do
35
- transition :from => 'processing', :to => 'failed'
35
+ transition :from => ['pending', 'processing'], :to => 'failed'
36
36
  end
37
37
  # With card payments this represents authorizing the payment
38
38
  event :pend do