Class: Spree::Product
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Spree::Product
- Extended by:
- FriendlyId
- Defined in:
- app/models/spree/product.rb,
app/models/spree/product/scopes.rb
Instance Attribute Summary collapse
-
#option_values_hash ⇒ Object
Returns the value of attribute option_values_hash.
-
#prototype_id ⇒ Object
Adding properties and option types on creation based on a chosen prototype.
Class Method Summary collapse
- .active(currency = nil) ⇒ Object
- .add_search_scope(name, &block) ⇒ Object
- .add_simple_scopes(scopes) ⇒ Object
-
.available(available_on = nil, currency = nil) ⇒ Object
Can’t use add_search_scope for this as it needs a default argument.
- .distinct_by_product_ids(sort_order = nil) ⇒ Object
- .like_any(fields, values) ⇒ Object
- .property_conditions(property) ⇒ Object
- .simple_scopes ⇒ Object
Instance Method Summary collapse
-
#available? ⇒ Boolean
determine if product is available.
-
#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” -> […].
-
#deleted? ⇒ Boolean
use deleted? rather than checking the attribute directly.
-
#duplicate ⇒ Object
for adding products which are closely related to existing ones define “duplicate_extra” for site-specific actions, eg for additional fields.
- #empty_option_values? ⇒ Boolean
-
#ensure_option_types_exist_for_values_hash ⇒ Object
Ensures option_types and product_option_types exist for keys in option_values_hash.
-
#has_variants? ⇒ Boolean
the master variant is not a member of the variants array.
-
#master ⇒ Object
Master variant may be deleted (i.e. when the product is deleted) which would make AR’s default finder return nil.
- #possible_promotions ⇒ Object
- #property(property_name) ⇒ Object
- #set_property(property_name, property_value) ⇒ Object
- #tax_category ⇒ Object
- #to_param ⇒ Object
- #total_on_hand ⇒ Object
-
#variants_and_option_values(current_currency = nil) ⇒ Object
Suitable for displaying only variants that has at least one option value.
Instance Attribute Details
#option_values_hash ⇒ Object
Returns the value of attribute option_values_hash.
87 88 89 |
# File 'app/models/spree/product.rb', line 87 def option_values_hash @option_values_hash end |
#prototype_id ⇒ Object
Adding properties and option types on creation based on a chosen prototype
113 114 115 |
# File 'app/models/spree/product.rb', line 113 def prototype_id @prototype_id end |
Class Method Details
.active(currency = nil) ⇒ Object
195 196 197 |
# File 'app/models/spree/product/scopes.rb', line 195 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 |
# 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_(.*)/) self.scope(name.to_s, -> { order("#{Product.quoted_table_name}.#{parts[2]} #{parts[1] == 'ascend' ? "ASC" : "DESC"}") }) end end |
.available(available_on = nil, currency = nil) ⇒ Object
Can’t use add_search_scope for this as it needs a default argument
190 191 192 |
# File 'app/models/spree/product/scopes.rb', line 190 def self.available(available_on = nil, currency = nil) joins(:master => :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.now) end |
.distinct_by_product_ids(sort_order = nil) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'app/models/spree/product/scopes.rb', line 204 def self.distinct_by_product_ids(sort_order=nil) if (ActiveRecord::Base.connection.adapter_name == 'PostgreSQL') sort_column = sort_order.split(" ").first # Don't allow sort_column, a variable coming from params, # to be anything but a column in the database if column_names.include?(sort_column) distinct_fields = ["id", sort_column].compact.join(",") select("DISTINCT ON(#{distinct_fields}) spree_products.*") else all end else select("DISTINCT spree_products.*") end end |
.like_any(fields, values) ⇒ Object
155 156 157 158 159 160 161 |
# File 'app/models/spree/product.rb', line 155 def self.like_any(fields, values) where fields.map { |field| values.map { |value| arel_table[field].matches("%#{value}%") }.inject(:or) }.inject(:or) end |
.property_conditions(property) ⇒ Object
30 31 32 33 34 35 36 37 |
# File 'app/models/spree/product/scopes.rb', line 30 def self.property_conditions(property) properties = Property.table_name conditions = case property when String then { "#{properties}.name" => property } when Property then { "#{properties}.id" => property.id } else { "#{properties}.id" => property.to_i } end end |
.simple_scopes ⇒ Object
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
determine if product is available. deleted products and products with nil or future available_on date are not available
144 145 146 |
# File 'app/models/spree/product.rb', line 144 def available? !(available_on.nil? || available_on.future?) && !deleted? 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” -> […]
150 151 152 153 |
# File 'app/models/spree/product.rb', line 150 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 |
#deleted? ⇒ Boolean
use deleted? rather than checking the attribute directly. this allows extensions to override deleted? if they want to provide their own definition.
137 138 139 |
# File 'app/models/spree/product.rb', line 137 def deleted? !!deleted_at end |
#duplicate ⇒ Object
for adding products which are closely related to existing ones define “duplicate_extra” for site-specific actions, eg for additional fields
129 130 131 132 |
# File 'app/models/spree/product.rb', line 129 def duplicate duplicator = ProductDuplicator.new(self) duplicator.duplicate end |
#empty_option_values? ⇒ Boolean
174 175 176 177 178 |
# File 'app/models/spree/product.rb', line 174 def empty_option_values? .empty? || .any? do |opt| opt.option_type.option_values.empty? end end |
#ensure_option_types_exist_for_values_hash ⇒ Object
Ensures option_types and product_option_types exist for keys in option_values_hash
119 120 121 122 123 124 125 |
# File 'app/models/spree/product.rb', line 119 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) 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
100 101 102 |
# File 'app/models/spree/product.rb', line 100 def has_variants? variants.any? end |
#master ⇒ Object
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.
215 216 217 |
# File 'app/models/spree/product.rb', line 215 def master super || variants_including_master.with_deleted.where(is_master: true).first end |
#possible_promotions ⇒ Object
199 200 201 202 |
# File 'app/models/spree/product.rb', line 199 def possible_promotions promotion_ids = promotion_rules.map(&:promotion_id).uniq Spree::Promotion.advertised.where(id: promotion_ids).reject(&:expired?) end |
#property(property_name) ⇒ Object
180 181 182 183 |
# File 'app/models/spree/product.rb', line 180 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
185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'app/models/spree/product.rb', line 185 def set_property(property_name, property_value) ActiveRecord::Base.transaction do # Works around spree_i18n #301 property = if Property.exists?(name: property_name) Property.where(name: property_name).first else Property.create(name: property_name, presentation: property_name) end product_property = ProductProperty.where(product: self, property: property).first_or_initialize product_property.value = property_value product_property.save! end end |
#tax_category ⇒ Object
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_param ⇒ Object
95 96 97 |
# File 'app/models/spree/product.rb', line 95 def to_param slug end |
#total_on_hand ⇒ Object
204 205 206 207 208 209 210 |
# File 'app/models/spree/product.rb', line 204 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
168 169 170 171 172 |
# File 'app/models/spree/product.rb', line 168 def variants_and_option_values(current_currency = nil) variants.includes(:option_values).active(current_currency).select do |variant| variant.option_values.any? end end |