Class: Spree::Product
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Spree::Product
- 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.
-
.group_by_products_id ⇒ Object
This method needs to be defined *as a method*, otherwise it will cause the problem shown in #1247.
- .like_any(fields, values) ⇒ Object
- .simple_scopes ⇒ Object
Instance Method Summary collapse
- #available? ⇒ Boolean
-
#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” -> […].
- #count_on_hand=(value) ⇒ Object
-
#delete ⇒ Object
override the delete method to set deleted_at value instead of actually deleting the product.
-
#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_stock? ⇒ Boolean
Returns true if there are inventory units (any variant) with “on_hand” state for this product Variants take precedence over master.
-
#has_variants? ⇒ Boolean
returns true if the product has any variants (the master variant is not a member of the variants array).
- #on_demand=(new_on_demand) ⇒ Object
-
#on_display? ⇒ Boolean
should product be displayed on products pages and search.
-
#on_hand ⇒ Object
returns the number of inventory units “on_hand” for this product.
-
#on_hand=(new_level) ⇒ Object
adjusts the “on_hand” inventory level for the product up or down to match the given new_level.
-
#on_sale? ⇒ Boolean
is this product actually available for purchase.
- #property(property_name) ⇒ Object
- #set_property(property_name, property_value) ⇒ Object
- #tax_category ⇒ Object
- #to_param ⇒ Object
- #variants_with_only_master ⇒ Object
Instance Attribute Details
#option_values_hash ⇒ Object
Returns the value of attribute option_values_hash.
73 74 75 |
# File 'app/models/spree/product.rb', line 73 def option_values_hash @option_values_hash end |
#prototype_id ⇒ Object
Adding properties and option types on creation based on a chosen prototype
160 161 162 |
# File 'app/models/spree/product.rb', line 160 def prototype_id @prototype_id end |
Class Method Details
.active(currency = nil) ⇒ Object
206 207 208 |
# File 'app/models/spree/product/scopes.rb', line 206 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
197 198 199 200 201 202 203 |
# File 'app/models/spree/product/scopes.rb', line 197 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_id ⇒ Object
This method needs to be defined *as a method*, otherwise it will cause the problem shown in #1247.
222 223 224 225 226 227 228 229 230 231 |
# File 'app/models/spree/product/scopes.rb', line 222 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
222 223 224 225 |
# File 'app/models/spree/product.rb', line 222 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_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
211 212 213 |
# File 'app/models/spree/product.rb', line 211 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” -> […]
217 218 219 220 |
# File 'app/models/spree/product.rb', line 217 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 |
#count_on_hand=(value) ⇒ Object
134 135 136 |
# File 'app/models/spree/product.rb', line 134 def count_on_hand=(value) raise I18n.t('exceptions.count_on_hand_setter') end |
#delete ⇒ Object
override the delete method to set deleted_at value instead of actually deleting the product.
154 155 156 157 |
# File 'app/models/spree/product.rb', line 154 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.
207 208 209 |
# File 'app/models/spree/product.rb', line 207 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
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'app/models/spree/product.rb', line 176 def duplicate p = self.dup p.name = 'COPY OF ' + name p.deleted_at = nil p.created_at = p.updated_at = nil p.taxons = taxons p.product_properties = product_properties.map { |q| r = q.dup; r.created_at = r.updated_at = nil; r } image_dup = lambda { |i| j = i.dup; j. = i..clone; j } variant = master.dup variant.sku = 'COPY OF ' + master.sku variant.deleted_at = nil variant.images = master.images.map { |i| image_dup.call i } variant.price = master.price variant.currency = master.currency p.master = variant # don't dup the actual variants, just the characterising types p.option_types = option_types if has_variants? # allow site to do some customization p.send(:duplicate_extra, self) if p.respond_to?(:duplicate_extra) p.save! p end |
#empty_option_values? ⇒ Boolean
227 228 229 230 231 |
# File 'app/models/spree/product.rb', line 227 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
166 167 168 169 170 171 172 |
# File 'app/models/spree/product.rb', line 166 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_stock? ⇒ Boolean
Returns true if there are inventory units (any variant) with “on_hand” state for this product Variants take precedence over master
140 141 142 |
# File 'app/models/spree/product.rb', line 140 def has_stock? has_variants? ? variants.any?(&:in_stock?) : master.in_stock? end |
#has_variants? ⇒ Boolean
returns true if the product has any variants (the master variant is not a member of the variants array)
101 102 103 |
# File 'app/models/spree/product.rb', line 101 def has_variants? variants.any? end |
#on_demand=(new_on_demand) ⇒ Object
128 129 130 131 132 |
# File 'app/models/spree/product.rb', line 128 def on_demand=(new_on_demand) raise 'cannot set on_demand of product with variants' if has_variants? && Spree::Config[:track_inventory_levels] master.on_demand = on_demand self[:on_demand] = new_on_demand end |
#on_display? ⇒ Boolean
should product be displayed on products pages and search
106 107 108 |
# File 'app/models/spree/product.rb', line 106 def on_display? has_stock? || Spree::Config[:show_zero_stock_products] end |
#on_hand ⇒ Object
returns the number of inventory units “on_hand” for this product
116 117 118 |
# File 'app/models/spree/product.rb', line 116 def on_hand has_variants? ? variants.sum(&:on_hand) : master.on_hand end |
#on_hand=(new_level) ⇒ Object
adjusts the “on_hand” inventory level for the product up or down to match the given new_level
121 122 123 124 125 126 |
# File 'app/models/spree/product.rb', line 121 def on_hand=(new_level) unless self.on_demand raise 'cannot set on_hand of product with variants' if has_variants? && Spree::Config[:track_inventory_levels] master.on_hand = new_level end end |
#on_sale? ⇒ Boolean
is this product actually available for purchase
111 112 113 |
# File 'app/models/spree/product.rb', line 111 def on_sale? has_stock? || Spree::Config[:allow_backorders] end |
#property(property_name) ⇒ Object
233 234 235 236 |
# File 'app/models/spree/product.rb', line 233 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
238 239 240 241 242 243 244 245 |
# File 'app/models/spree/product.rb', line 238 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_category ⇒ Object
144 145 146 147 148 149 150 |
# File 'app/models/spree/product.rb', line 144 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
96 97 98 |
# File 'app/models/spree/product.rb', line 96 def to_param permalink.present? ? permalink : (permalink_was || name.to_s.to_url) end |
#variants_with_only_master ⇒ Object
91 92 93 94 |
# File 'app/models/spree/product.rb', line 91 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 |