Class: Spree::Product

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

belongs_to_required_by_default, page, spree_base_scopes

Methods included from Spree::Preferences::Preferable

#clear_preferences, #default_preferences, #defined_preferences, #get_preference, #has_preference!, #has_preference?, #preference_default, #preference_type, #set_preference

Instance Attribute Details

#option_values_hashObject

Returns the value of attribute option_values_hash.



107
108
109
# File 'app/models/spree/product.rb', line 107

def option_values_hash
  @option_values_hash
end

#prototype_idObject

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



166
167
168
# File 'app/models/spree/product.rb', line 166

def prototype_id
  @prototype_id
end

Class Method Details

.like_any(fields, values) ⇒ Object



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

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

determine if product is available. deleted products and products with nil or future available_on date are not available

Returns:

  • (Boolean)


199
200
201
# File 'app/models/spree/product.rb', line 199

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

#backorderable?Boolean

Cant use short form block syntax due to github.com/Netflix/fast_jsonapi/issues/259

Returns:

  • (Boolean)


140
141
142
# File 'app/models/spree/product.rb', line 140

def backorderable?
  variants_including_master.any?(&:backorderable?)
end

#backordered?Boolean

determine if any variant (including master) is out of stock and backorderable

Returns:

  • (Boolean)


217
218
219
# File 'app/models/spree/product.rb', line 217

def backordered?
  variants_including_master.any?(&:backordered?)
end

#brandObject



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

def brand
  taxons.joins(:taxonomy).find_by(spree_taxonomies: { name: Spree.t(:taxonomy_brands_name) })
end

#can_supply?Boolean

determine if any variant (including master) can be supplied

Returns:

  • (Boolean)


212
213
214
# File 'app/models/spree/product.rb', line 212

def can_supply?
  variants_including_master.any?(&:can_supply?)
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” -> […]



223
224
225
226
227
# File 'app/models/spree/product.rb', line 223

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

#categoryObject



286
287
288
# File 'app/models/spree/product.rb', line 286

def category
  taxons.joins(:taxonomy).find_by(spree_taxonomies: { name: Spree.t(:taxonomy_categories_name) })
end

#default_variantObject



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

def default_variant
  has_variants? ? variants.first : master
end

#default_variant_idObject



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

def default_variant_id
  default_variant.id
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)


192
193
194
# File 'app/models/spree/product.rb', line 192

def deleted?
  !!deleted_at
end

#discontinue!Object



203
204
205
# File 'app/models/spree/product.rb', line 203

def discontinue!
  update_attribute(:discontinue_on, Time.current)
end

#discontinued?Boolean

Returns:

  • (Boolean)


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

def discontinued?
  !!discontinue_on && discontinue_on <= Time.current
end

#duplicateObject

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



184
185
186
187
# File 'app/models/spree/product.rb', line 184

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

#empty_option_values?Boolean

Returns:

  • (Boolean)


247
248
249
250
251
# File 'app/models/spree/product.rb', line 247

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



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

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)
  missing_option_type_ids = required_option_type_ids - option_type_ids
  missing_option_type_ids.each do |id|
    product_option_types.create(option_type_id: id)
  end
end

#find_or_build_masterObject



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

def find_or_build_master
  master || build_master
end

#has_variants?Boolean

the master variant is not a member of the variants array

Returns:

  • (Boolean)


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

def has_variants?
  variants.any?
end

#in_stock?Boolean

Cant use short form block syntax due to github.com/Netflix/fast_jsonapi/issues/259

Returns:

  • (Boolean)


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

def in_stock?
  variants_including_master.any?(&:in_stock?)
end

#masterObject

Master variant may be deleted (i.e. when the product is deleted) which would make AR’s default finder return nil. This is a stopgap for that little problem.



278
279
280
# File 'app/models/spree/product.rb', line 278

def master
  super || variants_including_master.with_deleted.find_by(is_master: true)
end

#property(property_name) ⇒ Object



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

def property(property_name)
  product_properties.joins(:property).find_by(spree_properties: { name: property_name }).try(:value)
end

#purchasable?Boolean

Cant use short form block syntax due to github.com/Netflix/fast_jsonapi/issues/259

Returns:

  • (Boolean)


130
131
132
# File 'app/models/spree/product.rb', line 130

def purchasable?
  variants_including_master.any?(&:purchasable?)
end

#set_property(property_name, property_value, property_presentation = property_name) ⇒ Object



257
258
259
260
261
262
263
264
265
# File 'app/models/spree/product.rb', line 257

def set_property(property_name, property_value, property_presentation = property_name)
  ApplicationRecord.transaction do
    # Works around spree_i18n #301
    property = Property.create_with(presentation: property_presentation).find_or_create_by(name: property_name)
    product_property = ProductProperty.where(product: self, property: property).first_or_initialize
    product_property.value = property_value
    product_property.save!
  end
end

#tax_categoryObject



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

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

#total_on_handObject



267
268
269
270
271
272
273
# File 'app/models/spree/product.rb', line 267

def total_on_hand
  if any_variants_not_track_inventory?
    Float::INFINITY
  else
    stock_items.sum(:count_on_hand)
  end
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



241
242
243
244
245
# File 'app/models/spree/product.rb', line 241

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