Class: Spree::Product

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/spree/product.rb,
app/models/spree/product/scopes.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#option_values_hashObject

Returns the value of attribute option_values_hash.



72
73
74
# File 'app/models/spree/product.rb', line 72

def option_values_hash
  @option_values_hash
end

#prototype_idObject

Adding properties and option types on creation based on a chosen prototype



120
121
122
# File 'app/models/spree/product.rb', line 120

def prototype_id
  @prototype_id
end

Class Method Details

.active(currency = nil) ⇒ Object



209
210
211
# File 'app/models/spree/product/scopes.rb', line 209

def self.active(currency = nil)
  not_deleted.available(nil, currency)
end

.add_search_scope(name, &block) ⇒ Object



7
8
9
10
# File 'app/models/spree/product/scopes.rb', line 7

def self.add_search_scope(name, &block)
  self.singleton_class.send(:define_method, name.to_sym, &block)
  search_scopes << name.to_sym
end

.add_simple_scopes(scopes) ⇒ Object



21
22
23
24
25
26
27
28
29
# File 'app/models/spree/product/scopes.rb', line 21

def self.add_simple_scopes(scopes)
  scopes.each do |name|
    # We should not define price scopes here, as they require something slightly different
    next if name.to_s.include?("master_price")
    parts = name.to_s.match(/(.*)_by_(.*)/)
    order_text = "#{Product.quoted_table_name}.#{parts[2]} #{parts[1] == 'ascend' ?  "ASC" : "DESC"}"
    self.scope(name.to_s, relation.order(order_text))
  end
end

.available(available_on = nil, currency = nil) ⇒ Object

Can’t use add_search_scope for this as it needs a default argument



200
201
202
203
204
205
206
# File 'app/models/spree/product/scopes.rb', line 200

def self.available(available_on = nil, currency = nil)
  scope = joins(:master => :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.now)
  unless Spree::Config.show_products_without_price
    scope = scope.where('spree_prices.currency' => currency || Spree::Config[:currency]).where('spree_prices.amount IS NOT NULL')
  end
  scope
end

.group_by_products_idObject

This method needs to be defined *as a method*, otherwise it will cause the problem shown in #1247.



220
221
222
223
224
225
226
227
228
229
# File 'app/models/spree/product/scopes.rb', line 220

def self.group_by_products_id
  if (ActiveRecord::Base.connection.adapter_name == 'PostgreSQL')
    # Need to check, otherwise `column_names` will fail
    if table_exists?
      group(column_names.map { |col_name| "#{table_name}.#{col_name}"})
    end
  else
    group("#{self.quoted_table_name}.id")
  end
end

.like_any(fields, values) ⇒ Object



159
160
161
162
# File 'app/models/spree/product.rb', line 159

def self.like_any(fields, values)
  where_str = fields.map { |field| Array.new(values.size, "#{self.quoted_table_name}.#{field} #{LIKE} ?").join(' OR ') }.join(' OR ')
  self.where([where_str, values.map { |value| "%#{value}%" } * fields.size].flatten)
end

.simple_scopesObject



12
13
14
15
16
17
18
19
# File 'app/models/spree/product/scopes.rb', line 12

def self.simple_scopes
  [
    :ascend_by_updated_at,
    :descend_by_updated_at,
    :ascend_by_name,
    :descend_by_name
  ]
end

Instance Method Details

#available?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'app/models/spree/product.rb', line 148

def available?
  !(available_on.nil? || available_on.future?)
end

#categorise_variants_from_option(opt_type) ⇒ Object

split variants list into hash which shows mapping of opt value onto matching variants eg categorise_variants_from_option(color) => -> […], “blue” -> […]



154
155
156
157
# File 'app/models/spree/product.rb', line 154

def categorise_variants_from_option(opt_type)
  return {} unless option_types.include?(opt_type)
  variants.active.group_by { |v| v.option_values.detect { |o| o.option_type == opt_type} }
end

#deleteObject

override the delete method to set deleted_at value instead of actually deleting the product.



114
115
116
117
# File 'app/models/spree/product.rb', line 114

def delete
  self.update_column(:deleted_at, Time.now)
  variants_including_master.update_all(deleted_at: Time.now)
end

#deleted?Boolean

use deleted? rather than checking the attribute directly. this allows extensions to override deleted? if they want to provide their own definition.

Returns:

  • (Boolean)


144
145
146
# File 'app/models/spree/product.rb', line 144

def deleted?
  !!deleted_at
end

#duplicateObject

for adding products which are closely related to existing ones define “duplicate_extra” for site-specific actions, eg for additional fields



136
137
138
139
# File 'app/models/spree/product.rb', line 136

def duplicate
  duplicator = ProductDuplicator.new(self)
  duplicator.duplicate
end

#empty_option_values?Boolean

Returns:

  • (Boolean)


175
176
177
178
179
# File 'app/models/spree/product.rb', line 175

def empty_option_values?
  options.empty? || options.any? do |opt|
    opt.option_type.option_values.empty?
  end
end

#ensure_option_types_exist_for_values_hashObject

Ensures option_types and product_option_types exist for keys in option_values_hash



126
127
128
129
130
131
132
# File 'app/models/spree/product.rb', line 126

def ensure_option_types_exist_for_values_hash
  return if option_values_hash.nil?
  option_values_hash.keys.map(&:to_i).each do |id|
    self.option_type_ids << id unless option_type_ids.include?(id)
    product_option_types.create({option_type_id: id}, without_protection: true) unless product_option_types.pluck(:option_type_id).include?(id)
  end
end

#has_variants?Boolean

the master variant is not a member of the variants array

Returns:

  • (Boolean)


100
101
102
# File 'app/models/spree/product.rb', line 100

def has_variants?
  variants.any?
end

#possible_promotionsObject



195
196
197
198
# File 'app/models/spree/product.rb', line 195

def possible_promotions
  promotion_ids = promotion_rules.map(&:activator_id).uniq
  Spree::Promotion.advertised.where(id: promotion_ids).reject(&:expired?)
end

#property(property_name) ⇒ Object



181
182
183
184
# File 'app/models/spree/product.rb', line 181

def property(property_name)
  return nil unless prop = properties.find_by_name(property_name)
  product_properties.find_by_property_id(prop.id).try(:value)
end

#set_property(property_name, property_value) ⇒ Object



186
187
188
189
190
191
192
193
# File 'app/models/spree/product.rb', line 186

def set_property(property_name, property_value)
  ActiveRecord::Base.transaction do
    property = Property.where(name: property_name).first_or_create!(presentation: property_name)
    product_property = ProductProperty.where(product_id: id, property_id: property.id).first_or_initialize
    product_property.value = property_value
    product_property.save!
  end
end

#tax_categoryObject



104
105
106
107
108
109
110
# File 'app/models/spree/product.rb', line 104

def tax_category
  if self[:tax_category_id].nil?
    TaxCategory.where(is_default: true).first
  else
    TaxCategory.find(self[:tax_category_id])
  end
end

#to_paramObject



95
96
97
# File 'app/models/spree/product.rb', line 95

def to_param
  permalink.present? ? permalink : (permalink_was || name.to_s.to_url)
end

#variants_and_option_values(current_currency = nil) ⇒ Object

Suitable for displaying only variants that has at least one option value. There may be scenarios where an option type is removed and along with it all option values. At that point all variants associated with only those values should not be displayed to frontend users. Otherwise it breaks the idea of having variants



169
170
171
172
173
# File 'app/models/spree/product.rb', line 169

def variants_and_option_values(current_currency = nil)
  variants.includes(:option_values).active(current_currency).select do |variant|
    variant.option_values.any?
  end
end

#variants_with_only_masterObject



90
91
92
93
# File 'app/models/spree/product.rb', line 90

def variants_with_only_master
  ActiveSupport::Deprecation.warn("[SPREE] Spree::Product#variants_with_only_master will be deprecated in Spree 1.3. Please use Spree::Product#master instead.")
  master
end