spree_core 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
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