Class: Product
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Product
- Includes:
- Scopes::Product
- Defined in:
- app/models/product.rb
Overview
PRODUCTS Products represent an entity for sale in a store. Products can have variations, called variants Products properties include description, permalink, availability,
shipping category, etc. that do not change by variant.
MASTER VARIANT Every product has one master variant, which stores master price and sku, size and weight, etc. The master variant does not have option values associated with it. Price, SKU, size, weight, etc. are all delegated to the master variant. Contains on_hand inventory levels only when there are no variants for the product.
VARIANTS All variants can access the product properties directly (via reverse delegation). Inventory units are tied to Variant. The master variant can have inventory units, but not option values. All other variants have option values and may have inventory units. Sum of on_hand each variant’s inventory level determine “on_hand” level for the product.
Constant Summary
Constants included from Scopes::Product
Scopes::Product::ATTRIBUTE_HELPER_METHODS, Scopes::Product::ORDERING, Scopes::Product::SCOPES
Instance Attribute Summary collapse
-
#prototype_id ⇒ Object
Adding properties and option types on creation based on a chosen prototype.
Class Method Summary collapse
-
.active ⇒ Object
RAILS 3 TODO - this scope doesn’t match the original 2.3.x version, needs attention (but it works).
- .available(available_on = nil) ⇒ Object
- .id_equals(input_id) ⇒ Object
- .like_any(fields, values) ⇒ Object
- .not_deleted ⇒ Object
- .on_hand ⇒ Object
- .taxons_name_eq(name) ⇒ Object
Instance Method Summary collapse
- #add_properties_and_option_types_from_prototype ⇒ Object
-
#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.
- #effective_tax_rate ⇒ Object
-
#has_stock? ⇒ Boolean
Returns true if there are inventory units (any variant) with “on_hand” state for this product.
-
#has_variants? ⇒ Boolean
returns true if the product has any variants (the master variant is not a member of the variants array).
-
#master_price ⇒ Object
———————————————————————————————————-.
- #master_price=(value) ⇒ Object
-
#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.
- #tax_category ⇒ Object
-
#to_param ⇒ Object
———————————————————————————————————- end deprecation region ———————————————————————————————————-.
- #variant ⇒ Object
- #variant_images ⇒ Object
- #variants? ⇒ Boolean
Methods included from Scopes::Product
arguments_for_scope_name, get_taxons, prepare_taxon_conditions, prepare_words
Instance Attribute Details
#prototype_id ⇒ Object
Adding properties and option types on creation based on a chosen prototype
178 179 180 |
# File 'app/models/product.rb', line 178 def prototype_id @prototype_id end |
Class Method Details
.active ⇒ Object
RAILS 3 TODO - this scope doesn’t match the original 2.3.x version, needs attention (but it works)
88 89 90 |
# File 'app/models/product.rb', line 88 def active not_deleted.available end |
.available(available_on = nil) ⇒ Object
83 84 85 |
# File 'app/models/product.rb', line 83 def available(available_on = nil) where(Product.arel_table[:available_on].lteq(available_on || Time.zone.now )) end |
.id_equals(input_id) ⇒ Object
96 97 98 |
# File 'app/models/product.rb', line 96 def id_equals(input_id) where(Product.arel_table[:id].eq(input_id)) end |
.like_any(fields, values) ⇒ Object
245 246 247 248 |
# File 'app/models/product.rb', line 245 def self.like_any(fields, values) where_str = fields.map{|field| Array.new(values.size, "products.#{field} #{LIKE} ?").join(' OR ') }.join(' OR ') self.where([where_str, values.map{|value| "%#{value}%"} * fields.size].flatten) end |
.not_deleted ⇒ Object
79 80 81 |
# File 'app/models/product.rb', line 79 def not_deleted where(Product.arel_table[:deleted_at].eq(nil)) end |
.on_hand ⇒ Object
92 93 94 |
# File 'app/models/product.rb', line 92 def on_hand where(Product.arel_table[:count_on_hand].gteq(0)) end |
.taxons_name_eq(name) ⇒ Object
100 101 102 |
# File 'app/models/product.rb', line 100 def taxons_name_eq(name) joins(:taxons).where(Taxon.arel_table[:name].eq(name)) end |
Instance Method Details
#add_properties_and_option_types_from_prototype ⇒ Object
183 184 185 186 187 188 189 190 |
# File 'app/models/product.rb', line 183 def add_properties_and_option_types_from_prototype if prototype_id && prototype = Prototype.find_by_id(prototype_id) prototype.properties.each do |property| product_properties.create(:property => property) end self.option_types = prototype.option_types end 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” -> […]
232 233 234 235 |
# File 'app/models/product.rb', line 232 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.
226 227 228 |
# File 'app/models/product.rb', line 226 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
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'app/models/product.rb', line 194 def duplicate p = self.dup p.name = 'COPY OF ' + self.name p.deleted_at = nil p.created_at = p.updated_at = nil p.taxons = self.taxons p.product_properties = self.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} p.images = self.images.map {|i| image_dup.call i} variant = self.master.dup variant.sku = 'COPY OF ' + self.master.sku variant.deleted_at = nil variant.images = self.master.images.map {|i| image_dup.call i} p.master = variant if self.has_variants? # don't dup the actual variants, just the characterising types p.option_types = self.option_types else end # allow site to do some customization p.send(:duplicate_extra, self) if p.respond_to?(:duplicate_extra) p.save! p end |
#effective_tax_rate ⇒ Object
237 238 239 240 241 242 243 |
# File 'app/models/product.rb', line 237 def effective_tax_rate if self.tax_category tax_category.effective_amount else TaxRate.default end end |
#has_stock? ⇒ Boolean
Returns true if there are inventory units (any variant) with “on_hand” state for this product
165 166 167 |
# File 'app/models/product.rb', line 165 def has_stock? master.in_stock? || variants.any?(&: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)
149 150 151 |
# File 'app/models/product.rb', line 149 def has_variants? variants.any? end |
#master_price ⇒ Object
The following methods are deprecated and will be removed in a future version of Spree
119 120 121 122 |
# File 'app/models/product.rb', line 119 def master_price warn "[DEPRECATION] `Product.master_price` is deprecated. Please use `Product.price` instead. (called from #{caller[0]})" self.price end |
#master_price=(value) ⇒ Object
124 125 126 127 |
# File 'app/models/product.rb', line 124 def master_price=(value) warn "[DEPRECATION] `Product.master_price=` is deprecated. Please use `Product.price=` instead. (called from #{caller[0]})" self.price = value end |
#on_hand ⇒ Object
returns the number of inventory units “on_hand” for this product
154 155 156 |
# File 'app/models/product.rb', line 154 def on_hand has_variants? ? variants.inject(0){|sum, v| sum + v.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
159 160 161 162 |
# File 'app/models/product.rb', line 159 def on_hand=(new_level) raise "cannot set on_hand of product with variants" if has_variants? && Spree::Config[:track_inventory_levels] master.on_hand = new_level end |
#tax_category ⇒ Object
169 170 171 172 173 174 175 |
# File 'app/models/product.rb', line 169 def tax_category if self[:tax_category_id].nil? TaxCategory.first(:conditions => {:is_default => true}) else TaxCategory.find(self[:tax_category_id]) end end |
#to_param ⇒ Object
end deprecation region
143 144 145 146 |
# File 'app/models/product.rb', line 143 def to_param return permalink if permalink.present? name.to_url end |
#variant ⇒ Object
134 135 136 137 |
# File 'app/models/product.rb', line 134 def variant warn "[DEPRECATION] `Product.variant` is deprecated. Please use `Product.master` instead. (called from #{caller[0]})" self.master end |
#variant_images ⇒ Object
61 62 63 |
# File 'app/models/product.rb', line 61 def variant_images Image.find_by_sql("SELECT assets.* FROM assets LEFT JOIN variants ON (variants.id = assets.viewable_id) WHERE (variants.product_id = #{self.id})") end |
#variants? ⇒ Boolean
129 130 131 132 |
# File 'app/models/product.rb', line 129 def variants? warn "[DEPRECATION] `Product.variants?` is deprecated. Please use `Product.has_variants?` instead. (called from #{caller[0]})" self.has_variants? end |