Class: Spree::Product

Inherits:
Base
  • Object
show all
Extended by:
FriendlyId
Includes:
Scopes, SoftDeletable
Defined in:
app/models/spree/product.rb,
app/models/spree/product/scopes.rb

Overview

Products represent an entity for sale in a store. Products can have variations, called variants. Product properties include description, permalink, availability, shipping category, etc. that do not change by variant.

Defined Under Namespace

Modules: Scopes

Constant Summary collapse

MASTER_ATTRIBUTES =
[
  :cost_currency,
  :cost_price,
  :depth,
  :height,
  :price,
  :sku,
  :track_inventory,
  :weight,
  :width,
]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Scopes

prepended

Methods inherited from Base

display_includes

Methods included from Core::Permalinks

#generate_permalink, #save_permalink

Instance Attribute Details

#option_values_hashObject

Returns the value of attribute option_values_hash.



127
128
129
# File 'app/models/spree/product.rb', line 127

def option_values_hash
  @option_values_hash
end

Class Method Details

.like_any(fields, values) ⇒ ActiveRecord::Relation

Poor man’s full text search.

Filters products to those which have any of the strings in values in any of the fields in fields.

Parameters:

  • fields (Array{String,Symbol})

    columns of the products table to search for values

  • values (Array{String})

    strings to search through fields for

Returns:

  • (ActiveRecord::Relation)

    scope with WHERE clause for search applied



219
220
221
222
223
224
# File 'app/models/spree/product.rb', line 219

def self.like_any(fields, values)
  conditions = fields.product(values).map do |(field, value)|
    arel_table[field].matches("%#{value}%")
  end
  where conditions.inject(:or)
end

Instance Method Details

#available?Boolean

Determines if product is available. A product is available if it has not been deleted, the available_on date is in the past and the discontinue_on date is nil or in the future.

Returns:

  • (Boolean)

    true if this product is available



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

def available?
  !deleted? && available_on&.past? && !discontinued?
end

#deleted?Boolean

Use for checking whether this product has been deleted. Provided for overriding the logic for determining if a product is deleted.

Returns:

  • (Boolean)

    true if this product is deleted



188
189
190
# File 'app/models/spree/product.rb', line 188

def deleted?
  !!deleted_at
end

#discontinued?Boolean

Determines if product is discontinued.

A product is discontinued if the discontinue_on date is not nil and in the past.

Returns:

  • (Boolean)

    true if this product is discontinued



207
208
209
# File 'app/models/spree/product.rb', line 207

def discontinued?
  !!discontinue_on&.past?
end

#duplicateSpree::Product

Creates a new product with the same attributes, variants, etc.

Returns:



179
180
181
182
# File 'app/models/spree/product.rb', line 179

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

#empty_option_values?Boolean

Returns true if there are no option values.

Returns:

  • (Boolean)

    true if there are no option values



254
255
256
# File 'app/models/spree/product.rb', line 254

def empty_option_values?
  options.empty? || !option_types.left_joins(:option_values).where('spree_option_values.id IS NULL').empty?
end

#ensure_option_types_exist_for_values_hashArray

Ensures option_types and product_option_types exist for keys in option_values_hash.

Returns:

  • (Array)

    the option_values



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

def ensure_option_types_exist_for_values_hash
  return if option_values_hash.nil?
  required_option_type_ids = option_values_hash.keys.map(&:to_i)
  self.option_type_ids |= required_option_type_ids
end

#find_or_build_masterObject



75
76
77
# File 'app/models/spree/product.rb', line 75

def find_or_build_master
  master || build_master
end

#find_variant_property_rule(option_value_ids) ⇒ Spree::VariantPropertyRule

Finds the variant property rule that matches the provided option value ids.

Parameters:

  • option_value_ids (Array<Integer>)

    list of option value ids

Returns:



301
302
303
304
305
# File 'app/models/spree/product.rb', line 301

def find_variant_property_rule(option_value_ids)
  variant_property_rules.find do |rule|
    rule.matches_option_value_ids?(option_value_ids)
  end
end

The gallery for the product, which represents all the images associated with it, including those on its variants

Returns:



311
312
313
# File 'app/models/spree/product.rb', line 311

def gallery
  @gallery ||= Spree::Config.product_gallery_class.new(self)
end

#has_variants?Boolean

Returns true if there are any variants.

Returns:

  • (Boolean)

    true if there are any variants



157
158
159
# File 'app/models/spree/product.rb', line 157

def has_variants?
  variants.any?
end

#possible_promotionsArray

Returns all advertised and not-rejected promotions.

Returns:

  • (Array)

    all advertised and not-rejected promotions



280
281
282
283
# File 'app/models/spree/product.rb', line 280

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

#property(property_name) ⇒ String

Returns the value of the given property. nil if property is undefined on this product.

Parameters:

  • property_name (String)

    the name of the property to find

Returns:

  • (String)

    the value of the given property. nil if property is undefined on this product



260
261
262
263
# File 'app/models/spree/product.rb', line 260

def property(property_name)
  return nil unless prop = properties.find_by(name: property_name)
  product_properties.find_by(property: prop).try(:value)
end

#set_property(property_name, property_value) ⇒ Object

Assigns the given value to the given property.

Parameters:

  • property_name (String)

    the name of the property

  • property_value (String)

    the property value



269
270
271
272
273
274
275
276
277
# File 'app/models/spree/product.rb', line 269

def set_property(property_name, property_value)
  ActiveRecord::Base.transaction do
    # Works around spree_i18n https://github.com/spree/spree/issues/301
    property = Spree::Property.create_with(presentation: property_name).find_or_create_by(name: property_name)
    product_property = Spree::ProductProperty.where(product: self, property: property).first_or_initialize
    product_property.value = property_value
    product_property.save!
  end
end

#tax_categorySpree::TaxCategory

Returns tax category for this product, or the default tax category.

Returns:



162
163
164
# File 'app/models/spree/product.rb', line 162

def tax_category
  super || Spree::TaxCategory.find_by(is_default: true)
end

#total_on_handFixnum, Infinity

The number of on-hand stock items; Infinity if any variant does not track inventory.

Returns:

  • (Fixnum, Infinity)


289
290
291
292
293
294
295
# File 'app/models/spree/product.rb', line 289

def total_on_hand
  if any_variants_not_track_inventory?
    Float::INFINITY
  else
    stock_items.sum(:count_on_hand)
  end
end

#variant_option_values_by_option_type(variant_scope = nil) ⇒ Hash<Spree::OptionType, Array<Spree::OptionValue>>

Groups all of the option values that are associated to the product’s variants, grouped by option type.

used to determine the applied option_types associated with the products variants grouped by option type

Parameters:

  • variant_scope (ActiveRecord_Associations_CollectionProxy) (defaults to: nil)

    scope to filter the variants

Returns:



242
243
244
245
246
247
248
249
250
251
# File 'app/models/spree/product.rb', line 242

def variant_option_values_by_option_type(variant_scope = nil)
  option_value_scope = Spree::OptionValuesVariant.joins(:variant)
    .where(spree_variants: { product_id: id })
  option_value_scope = option_value_scope.merge(variant_scope) if variant_scope
  option_value_ids = option_value_scope.distinct.pluck(:option_value_id)
  Spree::OptionValue.where(id: option_value_ids).
    includes(:option_type).
    order("#{Spree::OptionType.table_name}.position, #{Spree::OptionValue.table_name}.position").
    group_by(&:option_type)
end

#variants_and_option_values_for(pricing_options = Spree::Config.default_pricing_options) ⇒ Array<Spree::Variant>

Returns all variants with at least one option value.

Parameters:

  • pricing_options (Spree::Variant::PricingOptions) (defaults to: Spree::Config.default_pricing_options)

    the pricing options to search for, default: the default pricing options

Returns:

  • (Array<Spree::Variant>)

    all variants with at least one option value



229
230
231
232
233
# File 'app/models/spree/product.rb', line 229

def variants_and_option_values_for(pricing_options = Spree::Config.default_pricing_options)
  variants.includes(:option_values).with_prices(pricing_options).select do |variant|
    variant.option_values.any?
  end
end