spree_core 1.0.4 → 1.0.6

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 (57) hide show
  1. data/app/assets/javascripts/admin/admin.js.erb +18 -9
  2. data/app/assets/javascripts/admin/checkouts/edit.js +2 -2
  3. data/app/assets/stylesheets/admin/admin.css.erb +4 -2
  4. data/app/controllers/spree/admin/orders/customer_details_controller.rb +2 -2
  5. data/app/controllers/spree/admin/payments_controller.rb +1 -1
  6. data/app/models/spree/address.rb +6 -1
  7. data/app/models/spree/adjustment.rb +4 -2
  8. data/app/models/spree/calculator/default_tax.rb +2 -0
  9. data/app/models/spree/calculator/flat_percent_item_total.rb +4 -0
  10. data/app/models/spree/calculator/flat_rate.rb +4 -0
  11. data/app/models/spree/calculator/flexi_rate.rb +4 -0
  12. data/app/models/spree/calculator/per_item.rb +4 -0
  13. data/app/models/spree/calculator/price_sack.rb +6 -0
  14. data/app/models/spree/creditcard.rb +9 -6
  15. data/app/models/spree/gateway.rb +2 -0
  16. data/app/models/spree/image.rb +3 -0
  17. data/app/models/spree/mail_method.rb +7 -0
  18. data/app/models/spree/option_type.rb +2 -0
  19. data/app/models/spree/option_value.rb +2 -0
  20. data/app/models/spree/order.rb +1 -1
  21. data/app/models/spree/payment.rb +2 -0
  22. data/app/models/spree/payment_method.rb +2 -0
  23. data/app/models/spree/preference.rb +1 -0
  24. data/app/models/spree/product.rb +9 -1
  25. data/app/models/spree/product/scopes.rb +39 -34
  26. data/app/models/spree/product_property.rb +2 -0
  27. data/app/models/spree/property.rb +2 -0
  28. data/app/models/spree/prototype.rb +4 -0
  29. data/app/models/spree/return_authorization.rb +2 -0
  30. data/app/models/spree/role.rb +2 -0
  31. data/app/models/spree/shipment.rb +4 -0
  32. data/app/models/spree/shipping_category.rb +2 -0
  33. data/app/models/spree/shipping_method.rb +3 -0
  34. data/app/models/spree/state.rb +2 -0
  35. data/app/models/spree/tax_category.rb +2 -0
  36. data/app/models/spree/tax_rate.rb +2 -0
  37. data/app/models/spree/taxon.rb +2 -0
  38. data/app/models/spree/taxonomy.rb +2 -0
  39. data/app/models/spree/tracker.rb +2 -0
  40. data/app/models/spree/user.rb +1 -0
  41. data/app/models/spree/variant.rb +5 -0
  42. data/app/models/spree/zone.rb +2 -0
  43. data/app/models/spree/zone_member.rb +2 -0
  44. data/app/views/spree/admin/mail_methods/_form.html.erb +1 -1
  45. data/app/views/spree/admin/payment_methods/_form.html.erb +1 -1
  46. data/app/views/spree/admin/shared/_head.html.erb +3 -3
  47. data/app/views/spree/admin/tax_rates/index.html.erb +1 -1
  48. data/app/views/spree/admin/trackers/_form.html.erb +1 -1
  49. data/config/locales/en.yml +4 -0
  50. data/config/routes.rb +3 -3
  51. data/db/migrate/20120523061241_convert_sales_tax_to_default_tax.rb +9 -0
  52. data/lib/spree/core/calculated_adjustments.rb +6 -5
  53. data/lib/spree/core/ssl_requirement.rb +2 -2
  54. data/lib/spree/core/testing_support/fixtures.rb +4 -0
  55. data/lib/spree/core/version.rb +1 -1
  56. data/lib/spree/product_filters.rb +16 -7
  57. metadata +35 -36
@@ -1,3 +1,4 @@
1
+ <%# encoding: utf-8 %>
1
2
  //= require_self
2
3
 
3
4
  /**
@@ -59,8 +60,10 @@ format_product_autocomplete = function(item){
59
60
  }
60
61
 
61
62
  html += "<div><h4>" + product['name'] + "</h4>";
62
- html += "<span><strong>Sku: </strong>" + product['master']['sku'] + "</span>";
63
- html += "<span><strong>On Hand: </strong>" + product['count_on_hand'] + "</span></div>";
63
+ if (product['master']) {
64
+ html += "<span><strong><%= ::I18n.t(:sku) %>: </strong>" + product['master']['sku'] + "</span>";
65
+ }
66
+ html += "<span><strong><%= ::I18n.t(:on_hand) %>: </strong>" + product['count_on_hand'] + "</span></div>";
64
67
  }else{
65
68
  // variant
66
69
  var variant = item.data['variant'];
@@ -75,12 +78,12 @@ format_product_autocomplete = function(item){
75
78
  }
76
79
 
77
80
  name += " - " + $.map(variant['option_values'], function(option_value){
78
- return option_value["option_type"]["presentation"] + ": " + option_value['name'];
81
+ return option_value["option_type"]["presentation"] + ": " + option_value['presentation'];
79
82
  }).join(", ")
80
83
 
81
84
  html += "<div><h4>" + name + "</h4>";
82
- html += "<span><strong>Sku: </strong>" + variant['sku'] + "</span>";
83
- html += "<span><strong>On Hand: </strong>" + variant['count_on_hand'] + "</span></div>";
85
+ html += "<span><strong><%= ::I18n.t(:sku) %>: </strong>" + variant['sku'] + "</span>";
86
+ html += "<span><strong><%= ::I18n.t(:on_hand) %>: </strong>" + variant['count_on_hand'] + "</span></div>";
84
87
  }
85
88
 
86
89
  return html
@@ -97,7 +100,7 @@ prep_product_autocomplete_data = function(data){
97
100
 
98
101
  var name = product['name'];
99
102
  name += " - " + $.map(variant['option_values'], function(option_value){
100
- return option_value["option_type"]["presentation"] + ": " + option_value['name'];
103
+ return option_value["option_type"]["presentation"] + ": " + option_value['presentation'];
101
104
  }).join(", ");
102
105
 
103
106
  return {
@@ -120,7 +123,7 @@ $.fn.product_autocomplete = function(){
120
123
  return this.each(function() {
121
124
  $(this).autocomplete({
122
125
  source: function(request, response) {
123
- $.get("/admin/products.json?q=" + $('#add_product_name').val() + "&authenticity_token=" + encodeURIComponent($('meta[name=csrf-token]').attr("content")), function(data) {
126
+ $.get(ajax_urls.product_search_json + "?q=" + $('#add_product_name').val() + "&authenticity_token=" + encodeURIComponent($('meta[name=csrf-token]').attr("content")), function(data) {
124
127
  result = prep_product_autocomplete_data(data)
125
128
  response(result);
126
129
  });
@@ -171,8 +174,9 @@ $.fn.objectPicker = function(url){
171
174
  $.fn.productPicker = function(){
172
175
  $(this).objectPicker(ajax_urls.product_search_basic_json);
173
176
  }
177
+
174
178
  $.fn.userPicker = function(){
175
- $(this).objectPicker(ajax_urls.user_search_basic_json);
179
+ $(this).objectPicker(ajax_urls.user_search_json);
176
180
  }
177
181
 
178
182
  // Possible defunct
@@ -206,7 +210,12 @@ $(".observe_field").live('change', function() {
206
210
 
207
211
  handle_date_picker_fields = function(){
208
212
  $('.datepicker').datepicker({
209
- dateFormat: 'yy/mm/dd',
213
+ dateFormat: "<%= ::I18n.t(:format, :scope => 'spree.date_picker', :default => 'yy/mm/dd') %>",
214
+ dayNames: ["<%= ::I18n.t(:day_names, :scope => :date).join('","') %>"],
215
+ dayNamesMin: ["<%= ::I18n.t(:abbr_day_names, :scope => :date).join('","') %>"],
216
+ monthNames: ["<%= ::I18n.t(:month_names, :scope => :date).join('","') %>"],
217
+ prevText: '<%= ::I18n.t(:previous) %>',
218
+ nextText: '<%= ::I18n.t(:next) %>',
210
219
  showOn: "button",
211
220
  buttonImage: "<%= asset_path 'datepicker/cal.gif' %>",
212
221
  buttonImageOnly: true
@@ -46,7 +46,7 @@ $(document).ready(function(){
46
46
  minChars: 5,
47
47
  delay: 1500,
48
48
  source: function(request, response) {
49
- $.get("/admin/users.json?q=" + $("#customer_search").val() + "&authenticity_token=" + encodeURIComponent($('meta[name=csrf-token]').attr("content")), function(data) {
49
+ $.get(ajax_urls.user_search_json + "&q=" + $("#customer_search").val() + "&authenticity_token=" + encodeURIComponent($('meta[name=csrf-token]').attr("content")), function(data) {
50
50
  result = prep_user_autocomplete_data(data)
51
51
  response(result);
52
52
  });
@@ -78,6 +78,7 @@ $(document).ready(function(){
78
78
  $('#user_id').val(ui.item.data['id']);
79
79
  $('#guest_checkout_true').prop("checked", false);
80
80
  $('#guest_checkout_false').prop("checked", true);
81
+ $('#guest_checkout_false').prop("disabled", false);
81
82
  return true;
82
83
  }
83
84
  }).data("autocomplete")._renderItem = function(ul, item) {
@@ -107,7 +108,6 @@ $(document).ready(function(){
107
108
  $('#customer_search').val("");
108
109
  $('#user_id').val("");
109
110
  $('#checkout_email').val("");
110
- $('#guest_checkout_false').prop("disabled", true);
111
111
 
112
112
  $('#order_bill_address_attributes_firstname').val("");
113
113
  $('#order_bill_address_attributes_lastname').val("");
@@ -313,7 +313,7 @@ body {
313
313
  width: 24px; }
314
314
  button.small {
315
315
  font-size: 1em;
316
- padding: 0 10px 0 10px;
316
+ padding: 0 20px 0 0;
317
317
  }
318
318
  input.title,
319
319
  textarea {
@@ -597,7 +597,9 @@ span.handle{
597
597
  border: 1px solid #000;
598
598
  }
599
599
 
600
- .ac_results li h4 { width: 100% }
600
+ .ac_results li div { max-width: 90%; }
601
+
602
+ .ac_results li h4 { width: 100%; }
601
603
 
602
604
  .ac_results li:last-child { border: none; }
603
605
 
@@ -10,8 +10,8 @@ module Spree
10
10
  end
11
11
 
12
12
  def edit
13
- @order.build_bill_address(:country_id => Spree::Config[:default_country_id]) if @order.bill_address.nil?
14
- @order.build_ship_address(:country_id => Spree::Config[:default_country_id]) if @order.ship_address.nil?
13
+ @order.bill_address = Address.default if @order.bill_address.nil?
14
+ @order.ship_address = Address.default if @order.ship_address.nil?
15
15
  end
16
16
 
17
17
  def update
@@ -46,7 +46,7 @@ module Spree
46
46
 
47
47
  rescue Spree::Core::GatewayError => e
48
48
  flash[:error] = "#{e.message}"
49
- respond_with(@payment) { |format| format.html { redirect_to new_admin_payment_path(@order) } }
49
+ respond_with(@payment) { |format| format.html { redirect_to new_admin_order_payment_path(@order) } }
50
50
  end
51
51
  end
52
52
 
@@ -8,6 +8,11 @@ module Spree
8
8
  validates :firstname, :lastname, :address1, :city, :zipcode, :country, :phone, :presence => true
9
9
  validate :state_validate
10
10
 
11
+ attr_accessible :firstname, :lastname, :address1, :address2,
12
+ :city, :zipcode, :country_id, :state_id,
13
+ :country, :state, :phone, :state_name,
14
+ :company, :alternative_phone
15
+
11
16
  # disconnected since there's no code to display error messages yet OR matching client-side validation
12
17
  def phone_validate
13
18
  return if phone.blank?
@@ -60,7 +65,7 @@ module Spree
60
65
 
61
66
  def self.default
62
67
  country = Spree::Country.find_by_id(Spree::Config[:default_country_id])
63
- new :country_id => country.try(:id) || Country.first.id
68
+ new(:country => country || Country.first)
64
69
  end
65
70
 
66
71
  # can modify an address if it's not been used in an order (but checkouts controller has finer control)
@@ -22,6 +22,8 @@
22
22
  #
23
23
  module Spree
24
24
  class Adjustment < ActiveRecord::Base
25
+ attr_accessible :amount, :label
26
+
25
27
  belongs_to :adjustable, :polymorphic => true
26
28
  belongs_to :source, :polymorphic => true
27
29
  belongs_to :originator, :polymorphic => true
@@ -31,7 +33,7 @@ module Spree
31
33
 
32
34
  scope :tax, lambda { where(:originator_type => 'Spree::TaxRate', :adjustable_type => 'Spree::Order') }
33
35
  scope :price, lambda { where(:adjustable_type => 'Spree::LineItem') }
34
- scope :shipping, lambda { where(:label => I18n.t(:shipping)) }
36
+ scope :shipping, lambda { where(:originator_type => 'Spree::ShippingMethod') }
35
37
  scope :optional, where(:mandatory => false)
36
38
  scope :eligible, where(:eligible => true)
37
39
  scope :charge, where("amount >= 0")
@@ -74,7 +76,7 @@ module Spree
74
76
  private
75
77
 
76
78
  def update_adjustable
77
- adjustable.update! if adjustable.is_a? Order
79
+ adjustable.update! if adjustable.is_a? Spree::Order
78
80
  end
79
81
 
80
82
  end
@@ -1,3 +1,5 @@
1
+ require_dependency 'spree/calculator'
2
+
1
3
  module Spree
2
4
  class Calculator::DefaultTax < Calculator
3
5
  def self.description
@@ -1,7 +1,11 @@
1
+ require_dependency 'spree/calculator'
2
+
1
3
  module Spree
2
4
  class Calculator::FlatPercentItemTotal < Calculator
3
5
  preference :flat_percent, :decimal, :default => 0
4
6
 
7
+ attr_accessible :preferred_flat_percent
8
+
5
9
  def self.description
6
10
  I18n.t(:flat_percent)
7
11
  end
@@ -1,7 +1,11 @@
1
+ require_dependency 'spree/calculator'
2
+
1
3
  module Spree
2
4
  class Calculator::FlatRate < Calculator
3
5
  preference :amount, :decimal, :default => 0
4
6
 
7
+ attr_accessible :preferred_amount
8
+
5
9
  def self.description
6
10
  I18n.t(:flat_rate_per_order)
7
11
  end
@@ -1,9 +1,13 @@
1
+ require_dependency 'spree/calculator'
2
+
1
3
  module Spree
2
4
  class Calculator::FlexiRate < Calculator
3
5
  preference :first_item, :decimal, :default => 0.0
4
6
  preference :additional_item, :decimal, :default => 0.0
5
7
  preference :max_items, :integer, :default => 0
6
8
 
9
+ attr_accessible :preferred_first_item, :preferred_additional_item, :preferred_max_items
10
+
7
11
  def self.description
8
12
  I18n.t(:flexible_rate)
9
13
  end
@@ -1,7 +1,11 @@
1
+ require_dependency 'spree/calculator'
2
+
1
3
  module Spree
2
4
  class Calculator::PerItem < Calculator
3
5
  preference :amount, :decimal, :default => 0
4
6
 
7
+ attr_accessible :preferred_amount
8
+
5
9
  def self.description
6
10
  I18n.t(:flat_rate_per_item)
7
11
  end
@@ -1,9 +1,15 @@
1
+ require_dependency 'spree/calculator'
2
+
1
3
  module Spree
2
4
  class Calculator::PriceSack < Calculator
3
5
  preference :minimal_amount, :decimal, :default => 0
4
6
  preference :normal_amount, :decimal, :default => 0
5
7
  preference :discount_amount, :decimal, :default => 0
6
8
 
9
+ attr_accessible :preferred_minimal_amount,
10
+ :preferred_normal_amount,
11
+ :preferred_discount_amount
12
+
7
13
  def self.description
8
14
  I18n.t(:price_sack)
9
15
  end
@@ -11,6 +11,9 @@ module Spree
11
11
  validates :number, :presence => true, :unless => :has_payment_profile?, :on => :create
12
12
  validates :verification_value, :presence => true, :unless => :has_payment_profile?, :on => :create
13
13
 
14
+ attr_accessible :first_name, :last_name, :number, :verification_value, :year,
15
+ :month, :gateway_customer_profile_id, :gateway_payment_profile_id
16
+
14
17
  def process!(payment)
15
18
  if Spree::Config[:auto_capture]
16
19
  purchase(payment.amount.to_f, payment)
@@ -171,12 +174,12 @@ module Spree
171
174
  record_log payment, response
172
175
 
173
176
  if response.success?
174
- Payment.create(:order => payment.order,
175
- :source => payment,
176
- :payment_method => payment.payment_method,
177
- :amount => amount.abs * -1,
178
- :response_code => response.authorization,
179
- :state => 'completed')
177
+ Payment.create({ :order => payment.order,
178
+ :source => payment,
179
+ :payment_method => payment.payment_method,
180
+ :amount => amount.abs * -1,
181
+ :response_code => response.authorization,
182
+ :state => 'completed'}, :without_protection => true)
180
183
  else
181
184
  gateway_error(response)
182
185
  end
@@ -7,6 +7,8 @@ module Spree
7
7
  preference :server, :string, :default => 'test'
8
8
  preference :test_mode, :boolean, :default => true
9
9
 
10
+ attr_accessible :preferred_server, :preferred_test_mode
11
+
10
12
  def payment_source_class
11
13
  Creditcard
12
14
  end
@@ -1,6 +1,9 @@
1
1
  module Spree
2
2
  class Image < Asset
3
3
  validate :no_attachment_errors
4
+
5
+ attr_accessible :alt, :attachment, :position, :viewable_type, :viewable_id
6
+
4
7
  has_attached_file :attachment,
5
8
  :styles => { :mini => '48x48>', :small => '100x100>', :product => '240x240>', :large => '600x600>' },
6
9
  :default_style => :product,
@@ -16,6 +16,13 @@ module Spree
16
16
  preference :mail_bcc, :string, :default => 'spree@example.com'
17
17
  preference :intercept_email, :string, :default => nil
18
18
 
19
+ attr_accessible :environment, :preferred_enable_mail_delivery,
20
+ :preferred_mails_from, :preferred_mail_bcc,
21
+ :preferred_intercept_email, :preferred_mail_domain,
22
+ :preferred_mail_host, :preferred_mail_port,
23
+ :preferred_secure_connection_type, :preferred_mail_auth_type,
24
+ :preferred_smtp_username, :preferred_smtp_password
25
+
19
26
  validates :environment, :presence => true
20
27
 
21
28
  def self.current
@@ -4,6 +4,8 @@ module Spree
4
4
  has_many :product_option_types, :dependent => :destroy
5
5
  has_and_belongs_to_many :prototypes, :join_table => 'spree_option_types_prototypes'
6
6
 
7
+ attr_accessible :name, :presentation, :option_values_attributes
8
+
7
9
  validates :name, :presentation, :presence => true
8
10
  default_scope :order => "#{self.table_name}.position"
9
11
 
@@ -3,5 +3,7 @@ module Spree
3
3
  belongs_to :option_type
4
4
  acts_as_list :scope => :option_type
5
5
  has_and_belongs_to_many :variants, :join_table => 'spree_option_values_variants'
6
+
7
+ attr_accessible :name, :presentation
6
8
  end
7
9
  end
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class Order < ActiveRecord::Base
3
3
  attr_accessible :line_items, :bill_address_attributes, :ship_address_attributes, :payments_attributes,
4
- :ship_address, :bill_address, :line_items_attributes,
4
+ :ship_address, :bill_address, :line_items_attributes, :number,
5
5
  :shipping_method_id, :email, :use_billing, :special_instructions
6
6
 
7
7
  belongs_to :user
@@ -15,6 +15,8 @@ module Spree
15
15
  attr_accessor :source_attributes
16
16
  after_initialize :build_source
17
17
 
18
+ attr_accessible :amount, :payment_method_id, :source_attributes
19
+
18
20
  scope :from_creditcard, where(:source_type => 'Spree::Creditcard')
19
21
  scope :with_state, lambda { |s| where(:state => s) }
20
22
  scope :completed, with_state('completed')
@@ -5,6 +5,8 @@ module Spree
5
5
 
6
6
  scope :production, where(:environment => 'production')
7
7
 
8
+ attr_accessible :name, :description, :environment, :display_on, :active
9
+
8
10
  def self.providers
9
11
  Rails.application.config.spree.payment_methods
10
12
  end
@@ -1,4 +1,5 @@
1
1
  class Spree::Preference < ActiveRecord::Base
2
+ attr_accessible :name, :key, :value_type, :value
2
3
 
3
4
  validates :key, :presence => true
4
5
  validates :value_type, :presence => true
@@ -64,6 +64,14 @@ module Spree
64
64
 
65
65
  validates :name, :price, :permalink, :presence => true
66
66
 
67
+ attr_accessible :name, :description, :available_on, :permalink, :meta_description,
68
+ :meta_keywords, :price, :sku, :deleted_at, :prototype_id,
69
+ :option_values_hash, :on_hand, :weight, :height, :width, :depth,
70
+ :shipping_category_id, :tax_category_id, :product_properties_attributes,
71
+ :variants_attributes, :taxon_ids
72
+
73
+ attr_accessible :cost_price if Variant.table_exists? && Variant.column_names.include?('cost_price')
74
+
67
75
  accepts_nested_attributes_for :product_properties, :allow_destroy => true, :reject_if => lambda { |pp| pp[:property_name].blank? }
68
76
 
69
77
  make_permalink
@@ -119,7 +127,7 @@ module Spree
119
127
  def add_properties_and_option_types_from_prototype
120
128
  if prototype_id && prototype = Spree::Prototype.find_by_id(prototype_id)
121
129
  prototype.properties.each do |property|
122
- product_properties.create(:property => property)
130
+ product_properties.create({:property => property}, :without_protection => true)
123
131
  end
124
132
  self.option_types = prototype.option_types
125
133
  end
@@ -1,5 +1,14 @@
1
1
  module Spree
2
2
  class Product < ActiveRecord::Base
3
+ cattr_accessor :search_scopes do
4
+ []
5
+ end
6
+
7
+ def self.add_search_scope(name, &block)
8
+ self.singleton_class.send(:define_method, name.to_sym, &block)
9
+ search_scopes << name.to_sym
10
+ end
11
+
3
12
  def self.simple_scopes
4
13
  [
5
14
  :ascend_by_updated_at,
@@ -22,37 +31,34 @@ module Spree
22
31
  self.scope(name.to_s, relation.order(order_text))
23
32
  end
24
33
 
25
- def self.ascend_by_master_price
34
+ add_search_scope :ascend_by_master_price do
26
35
  joins(:variants_with_only_master).order("#{variant_table_name}.price ASC")
27
36
  end
28
37
 
29
- def self.descend_by_master_price
38
+ add_search_scope :descend_by_master_price do
30
39
  joins(:variants_with_only_master).order("#{variant_table_name}.price DESC")
31
40
  end
32
41
 
33
42
  # Ryan Bates - http://railscasts.com/episodes/112
34
43
  # general merging of conditions, names following the searchlogic pattern
35
- scope :conditions, lambda { |*args| { :conditions => args } }
44
+ add_search_scope :conditions do |*args|
45
+ where(args)
46
+ end
36
47
 
37
48
  # conditions_all is a more descriptively named enhancement of the above
38
- scope :conditions_all, lambda { |*args| { :conditions => [args].flatten } }
39
-
40
- # forming the disjunction of a list of conditions (as strings)
41
- scope :conditions_any, lambda { |*args|
42
- args = [args].flatten
43
- raise "non-strings in conditions_any" unless args.all? { |s| s.is_a? String }
44
- { :conditions => args.map { |c| "(#{c})"}.join(" OR ") }
45
- }
49
+ add_search_scope :conditions_all do |*args|
50
+ where([args].flatten)
51
+ end
46
52
 
47
- def self.price_between(low, high)
53
+ add_search_scope :price_between do |low, high|
48
54
  joins(:master).where(Variant.table_name => { :price => low..high })
49
55
  end
50
56
 
51
- def self.master_price_lte(price)
57
+ add_search_scope :master_price_lte do |price|
52
58
  joins(:master).where("#{variant_table_name}.price <= ?", price)
53
59
  end
54
60
 
55
- def self.master_price_gte(price)
61
+ add_search_scope :master_price_gte do |price|
56
62
  joins(:master).where("#{variant_table_name}.price >= ?", price)
57
63
  end
58
64
 
@@ -60,7 +66,7 @@ module Spree
60
66
  # If you need products only within one taxon use
61
67
  #
62
68
  # Spree::Product.taxons_id_eq(x)
63
- def self.in_taxon(taxon)
69
+ add_search_scope :in_taxon do |taxon|
64
70
  joins(:taxons).where(Taxon.table_name => { :id => taxon.self_and_descendants.map(&:id) })
65
71
  end
66
72
 
@@ -68,8 +74,7 @@ module Spree
68
74
  # If you need products only within one taxon use
69
75
  #
70
76
  # Spree::Product.taxons_id_eq([x,y])
71
- #
72
- def self.in_taxons(*taxons)
77
+ add_search_scope :in_taxons do |*taxons|
73
78
  taxons = get_taxons(taxons)
74
79
  taxons.first ? prepare_taxon_conditions(taxons) : scoped
75
80
  end
@@ -79,7 +84,7 @@ module Spree
79
84
  end
80
85
 
81
86
  # a scope that finds all products having property specified by name, object or id
82
- def self.with_property(property)
87
+ add_search_scope :with_property do |property|
83
88
  properties = Property.table_name
84
89
  conditions = case property
85
90
  when String then { "#{properties}.name" => property }
@@ -92,7 +97,7 @@ module Spree
92
97
 
93
98
  # a simple test for product with a certain property-value pairing
94
99
  # note that it can test for properties with NULL values, but not for absent values
95
- def self.with_property_value(property, value)
100
+ add_search_scope :with_property_value do |property, value|
96
101
  properties = Spree::Property.table_name
97
102
  conditions = case property
98
103
  when String then ["#{properties}.name = ?", property]
@@ -104,8 +109,7 @@ module Spree
104
109
  joins(:properties).where(conditions)
105
110
  end
106
111
 
107
- # a scope that finds all products having an option_type specified by name, object or id
108
- def self.with_option(option)
112
+ add_search_scope :with_option do |option|
109
113
  option_types = OptionType.table_name
110
114
  conditions = case option
111
115
  when String then { "#{option_types}.name" => option }
@@ -116,8 +120,7 @@ module Spree
116
120
  joins(:option_types).where(conditions)
117
121
  end
118
122
 
119
- # a scope that finds all products having an option value specified by name, object or id
120
- def self.with_option_value(option, value)
123
+ add_search_scope :with_option_value do |option, value|
121
124
  option_values = OptionValue.table_name
122
125
  option_type_id = case option
123
126
  when String then OptionType.find_by_name(option) || option.to_i
@@ -132,30 +135,30 @@ module Spree
132
135
  # Finds all products which have either:
133
136
  # 1) have an option value with the name matching the one given
134
137
  # 2) have a product property with a value matching the one given
135
- def self.with(value)
138
+ add_search_scope :with do |value|
136
139
  includes(:variants_including_master => :option_values).
137
140
  includes(:product_properties).
138
141
  where("#{OptionValue.table_name}.name = ? OR #{ProductProperty.table_name}.value = ?", value, value)
139
142
  end
140
143
 
141
144
  # Finds all products that have a name containing the given words.
142
- def self.in_name(words)
145
+ add_search_scope :in_name do |words|
143
146
  like_any([:name], prepare_words(words))
144
147
  end
145
148
 
146
149
  # Finds all products that have a name or meta_keywords containing the given words.
147
- def self.in_name_or_keywords(words)
150
+ add_search_scope :in_name_or_keywords do |words|
148
151
  like_any([:name, :meta_keywords], prepare_words(words))
149
152
  end
150
153
 
151
154
  # Finds all products that have a name, description, meta_description or meta_keywords containing the given keywords.
152
- def self.in_name_or_description(words)
155
+ add_search_scope :in_name_or_description do |words|
153
156
  like_any([:name, :description, :meta_description, :meta_keywords], prepare_words(words))
154
157
  end
155
158
 
156
159
  # Finds all products that have the ids matching the given collection of ids.
157
160
  # Alternatively, you could use find(collection_of_ids), but that would raise an exception if one product couldn't be found
158
- def self.with_ids(*ids)
161
+ add_search_scope :with_ids do |*ids|
159
162
  where(:id => ids)
160
163
  end
161
164
 
@@ -167,7 +170,7 @@ module Spree
167
170
  #
168
171
  # :joins => "LEFT OUTER JOIN (SELECT line_items.variant_id as vid, COUNT(*) as cnt FROM line_items GROUP BY line_items.variant_id) AS popularity_count ON variants.id = vid",
169
172
  # :order => 'COALESCE(cnt, 0) DESC'
170
- def self.descend_by_popularity
173
+ add_search_scope :descend_by_popularity do
171
174
  joins(:master).
172
175
  order(%Q{
173
176
  COALESCE((
@@ -185,25 +188,26 @@ module Spree
185
188
  })
186
189
  end
187
190
 
188
- def self.not_deleted
191
+ add_search_scope :not_deleted do
189
192
  where(:deleted_at => nil)
190
193
  end
191
194
 
195
+ # Can't use add_search_scope for this as it needs a default argument
192
196
  def self.available(available_on = nil)
193
197
  where('available_on <= ?', available_on || Time.now)
194
198
  end
199
+ search_scopes << :available
195
200
 
196
- #RAILS 3 TODO - this scope doesn't match the original 2.3.x version, needs attention (but it works)
197
- def self.active
201
+ add_search_scope :active do
198
202
  not_deleted.available
199
203
  end
200
204
 
201
- def self.on_hand
205
+ add_search_scope :on_hand do
202
206
  variants_table = Variant.table_name
203
207
  where("#{table_name}.id in (select product_id from #{variants_table} where product_id = #{table_name}.id group by product_id having sum(count_on_hand) > 0)")
204
208
  end
205
209
 
206
- def self.taxons_name_eq(name)
210
+ add_search_scope :taxons_name_eq do |name|
207
211
  joins(:taxons).where(Taxon.arel_table[:name].eq(name))
208
212
  end
209
213
 
@@ -215,6 +219,7 @@ module Spree
215
219
  scope :group_by_products_id, { :group => "#{self.quoted_table_name}.id" }
216
220
  end
217
221
  search_methods :group_by_products_id
222
+ search_scopes << :group_by_products_id
218
223
 
219
224
  private
220
225
  def self.variant_table_name