Class: Spree::Variant
- Includes:
- DefaultPrice
- Defined in:
- app/models/spree/variant.rb,
app/models/spree/variant/scopes.rb
Overview
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. 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.
Class Method Summary collapse
-
.active(currency = nil) ⇒ ActiveRecord::Relation
Returns variants that are not deleted and have a price in the given currency.
-
.has_option(option_type, *option_values) ⇒ Object
(also: has_options)
Returns variants that match a given option value.
-
.in_stock(stock_locations = nil) ⇒ ActiveRecord::Relation
Returns variants that are in stock.
Instance Method Summary collapse
-
#amount_in(currency) ⇒ Float
Fetches the price amount in the specified currency.
-
#can_supply?(quantity = 1) ⇒ Boolean
True if the desired quantity can be supplied.
-
#cost_price=(price) ⇒ Bignum
Sets the cost_price for the variant.
-
#deleted? ⇒ Boolean
Returns whether this variant has been deleted.
-
#descriptive_name ⇒ String
Generates a verbose name for the variant, appending ‘Master’ if it is a master variant, otherwise a list of its option values.
-
#display_image(fallback: true) ⇒ Spree::Image
Image that can be used for the variant.
-
#exchange_name ⇒ String
Determines the name of an Exchange variant.
-
#in_stock? ⇒ Boolean
True if there is stock on-hand for the variant.
-
#is_backorderable? ⇒ Boolean
True if this variant can be backordered.
-
#name_and_sku ⇒ String
Generates a friendly name and sku string.
-
#on_backorder ⇒ Fixnum
Counts the number of units currently on backorder for this variant.
-
#option_value(opt_name) ⇒ String
Fetches the option value for the given option name.
-
#options=(options = {}) ⇒ Object
Assign given options hash to option values.
-
#options_text ⇒ String
Creates a sentence out of the variant’s (sorted) option values.
-
#price_in(currency) ⇒ Spree::Price
Converts the variant’s price to the given currency.
-
#price_modifier_amount(options = {}) ⇒ Fixnum
Calculates the sum of the specified price modifiers.
-
#price_modifier_amount_in(currency, options = {}) ⇒ Fixnum
Calculates the sum of the specified price modifiers in the specified currency.
-
#product ⇒ Spree::Product
Override ActiveRecord finder to function even if the product has been deleted.
-
#set_option_value(opt_name, opt_value) ⇒ Object
Sets an option type and value for the given name and value.
-
#should_track_inventory? ⇒ Boolean
Shortcut method to determine if inventory tracking is enabled for this variant.
-
#sku_and_options_text ⇒ String
Generates a string of the SKU and a list of all the option values.
-
#tax_category ⇒ Spree::TaxCategory
The variant’s tax category.
-
#total_on_hand ⇒ Fixnum
Fetches the on-hand quantity of the variant.
-
#variant_properties ⇒ Array<Spree::VariantPropertyRuleValue>
Determines the variant’s property values by verifying which of the product’s variant property rules apply to itself.
-
#weight=(weight) ⇒ Bignum
Sets the weight for the variant.
Methods inherited from Base
Methods included from Preferences::Preferable
#default_preferences, #defined_preferences, #get_preference, #has_preference!, #has_preference?, #preference_default, #preference_type, #set_preference
Class Method Details
.active(currency = nil) ⇒ ActiveRecord::Relation
Returns variants that are not deleted and have a price in the given currency.
82 83 84 |
# File 'app/models/spree/variant.rb', line 82 def self.active(currency = nil) joins(:prices).where(deleted_at: nil).where('spree_prices.currency' => currency || Spree::Config[:currency]).where('spree_prices.amount IS NOT NULL') end |
.has_option(option_type, *option_values) ⇒ Object Also known as: has_options
Returns variants that match a given option value
Example:
product.variants_including_master.has_option(OptionType.find_by(name: ‘shoe-size’), OptionValue.find_by(name: ‘8’))
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'app/models/spree/variant/scopes.rb', line 14 def has_option(option_type, *option_values) option_types = OptionType.table_name option_type_conditions = case option_type when OptionType then { "#{option_types}.name" => option_type.name } when String then { "#{option_types}.name" => option_type } else { "#{option_types}.id" => option_type } end relation = joins(:option_values => :option_type).where(option_type_conditions) option_values_conditions = option_values.each do |option_value| option_value_conditions = case option_value when OptionValue then { "#{OptionValue.table_name}.name" => option_value.name } when String then { "#{OptionValue.table_name}.name" => option_value } else { "#{OptionValue.table_name}.id" => option_value } end relation = relation.where(option_value_conditions) end relation end |
.in_stock(stock_locations = nil) ⇒ ActiveRecord::Relation
Returns variants that are in stock. When stock locations are provided as a parameter, the scope is limited to variants that are in stock in the provided stock locations.
66 67 68 69 70 71 72 |
# File 'app/models/spree/variant.rb', line 66 def self.in_stock(stock_locations = nil) in_stock_variants = joins(:stock_items).where(Spree::StockItem.arel_table[:count_on_hand].gt(0).or(arel_table[:track_inventory].eq(false))) if stock_locations.present? in_stock_variants = in_stock_variants.where(spree_stock_items: { stock_location_id: stock_locations.map(&:id) }) end in_stock_variants end |
Instance Method Details
#amount_in(currency) ⇒ Float
Fetches the price amount in the specified currency.
232 233 234 |
# File 'app/models/spree/variant.rb', line 232 def amount_in(currency) price_in(currency).try(:amount) end |
#can_supply?(quantity = 1) ⇒ Boolean
Returns true if the desired quantity can be supplied.
295 296 297 |
# File 'app/models/spree/variant.rb', line 295 def can_supply?(quantity=1) Spree::Stock::Quantifier.new(self).can_supply?(quantity) end |
#cost_price=(price) ⇒ Bignum
Sets the cost_price for the variant.
99 100 101 |
# File 'app/models/spree/variant.rb', line 99 def cost_price=(price) self[:cost_price] = Spree::LocalizedNumber.parse(price) if price.present? end |
#deleted? ⇒ Boolean
Returns whether this variant has been deleted. Provided as a method of overriding the logic for determining if a variant is deleted.
157 158 159 |
# File 'app/models/spree/variant.rb', line 157 def deleted? !!deleted_at end |
#descriptive_name ⇒ String
Generates a verbose name for the variant, appending ‘Master’ if it is a master variant, otherwise a list of its option values.
149 150 151 |
# File 'app/models/spree/variant.rb', line 149 def descriptive_name is_master? ? name + ' - Master' : name + ' - ' + end |
#display_image(fallback: true) ⇒ Spree::Image
Image that can be used for the variant.
Will first search for images on the variant. If it doesn’t find any, it’ll fallback to any variant image (unless fallback
is false
) or to a new Image.
323 324 325 |
# File 'app/models/spree/variant.rb', line 323 def display_image(fallback: true) images.first || (fallback && product.variant_images.first) || Spree::Image.new end |
#exchange_name ⇒ String
Determines the name of an Exchange variant.
141 142 143 |
# File 'app/models/spree/variant.rb', line 141 def exchange_name is_master? ? name : end |
#in_stock? ⇒ Boolean
Returns true if there is stock on-hand for the variant.
287 288 289 290 291 |
# File 'app/models/spree/variant.rb', line 287 def in_stock? Rails.cache.fetch(in_stock_cache_key) do total_on_hand > 0 end end |
#is_backorderable? ⇒ Boolean
Returns true if this variant can be backordered.
119 120 121 |
# File 'app/models/spree/variant.rb', line 119 def is_backorderable? Spree::Stock::Quantifier.new(self).backorderable? end |
#name_and_sku ⇒ String
Generates a friendly name and sku string.
275 276 277 |
# File 'app/models/spree/variant.rb', line 275 def name_and_sku "#{name} - #{sku}" end |
#on_backorder ⇒ Fixnum
Counts the number of units currently on backorder for this variant.
114 115 116 |
# File 'app/models/spree/variant.rb', line 114 def on_backorder inventory_units.with_state('backordered').size end |
#option_value(opt_name) ⇒ String
Fetches the option value for the given option name.
216 217 218 |
# File 'app/models/spree/variant.rb', line 216 def option_value(opt_name) self.option_values.detect { |o| o.option_type.name == opt_name }.try(:presentation) end |
#options=(options = {}) ⇒ Object
Assign given options hash to option values.
172 173 174 175 176 |
# File 'app/models/spree/variant.rb', line 172 def ( = {}) .each do |option| set_option_value(option[:name], option[:value]) end end |
#options_text ⇒ String
Creates a sentence out of the variant’s (sorted) option values.
126 127 128 129 130 131 132 133 134 135 136 |
# File 'app/models/spree/variant.rb', line 126 def values = self.option_values.includes(:option_type).sort_by do |option_value| option_value.option_type.position end values.to_a.map! do |ov| "#{ov.option_type.presentation}: #{ov.presentation}" end values.to_sentence({ words_connector: ", ", two_words_connector: ", " }) end |
#price_in(currency) ⇒ Spree::Price
Converts the variant’s price to the given currency.
224 225 226 |
# File 'app/models/spree/variant.rb', line 224 def price_in(currency) prices.detect{ |price| price.currency == currency && price.is_default } || Spree::Price.new(variant_id: self.id, currency: currency) end |
#price_modifier_amount(options = {}) ⇒ Fixnum
Calculates the sum of the specified price modifiers.
259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'app/models/spree/variant.rb', line 259 def price_modifier_amount( = {}) return 0 unless .present? .keys.map { |key| m = "#{[key]}_price_modifier_amount".to_sym if self.respond_to? m self.send(m, [key]) else 0 end }.sum end |
#price_modifier_amount_in(currency, options = {}) ⇒ Fixnum
Calculates the sum of the specified price modifiers in the specified currency.
242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'app/models/spree/variant.rb', line 242 def price_modifier_amount_in(currency, = {}) return 0 unless .present? .keys.map { |key| m = "#{key}_price_modifier_amount_in".to_sym if self.respond_to? m self.send(m, currency, [key]) else 0 end }.sum end |
#product ⇒ Spree::Product
Override ActiveRecord finder to function even if the product has been deleted.
165 166 167 |
# File 'app/models/spree/variant.rb', line 165 def product Spree::Product.unscoped { super } end |
#set_option_value(opt_name, opt_value) ⇒ Object
Sets an option type and value for the given name and value.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'app/models/spree/variant.rb', line 182 def set_option_value(opt_name, opt_value) # no option values on master return if self.is_master option_type = Spree::OptionType.where(name: opt_name).first_or_initialize do |o| o.presentation = opt_name o.save! end current_value = self.option_values.detect { |o| o.option_type.name == opt_name } unless current_value.nil? return if current_value.name == opt_value self.option_values.delete(current_value) else # then we have to check to make sure that the product has the option type unless self.product.option_types.include? option_type self.product.option_types << option_type end end option_value = Spree::OptionValue.where(option_type_id: option_type.id, name: opt_value).first_or_initialize do |o| o.presentation = opt_value o.save! end self.option_values << option_value self.save end |
#should_track_inventory? ⇒ Boolean
Shortcut method to determine if inventory tracking is enabled for this variant. This considers both variant tracking flag and site-wide inventory tracking settings.
311 312 313 |
# File 'app/models/spree/variant.rb', line 311 def should_track_inventory? self.track_inventory? && Spree::Config.track_inventory_levels end |
#sku_and_options_text ⇒ String
Generates a string of the SKU and a list of all the option values.
282 283 284 |
# File 'app/models/spree/variant.rb', line 282 def "#{sku} #{}".strip end |
#tax_category ⇒ Spree::TaxCategory
Returns the variant’s tax category.
87 88 89 90 91 92 93 |
# File 'app/models/spree/variant.rb', line 87 def tax_category if self[:tax_category_id].nil? product.tax_category else TaxCategory.find(self[:tax_category_id]) end end |
#total_on_hand ⇒ Fixnum
Fetches the on-hand quantity of the variant.
302 303 304 |
# File 'app/models/spree/variant.rb', line 302 def total_on_hand Spree::Stock::Quantifier.new(self).total_on_hand end |
#variant_properties ⇒ Array<Spree::VariantPropertyRuleValue>
Determines the variant’s property values by verifying which of the product’s variant property rules apply to itself.
331 332 333 334 335 |
# File 'app/models/spree/variant.rb', line 331 def variant_properties self.product.variant_property_rules.map do |rule| rule.values if rule.applies_to_variant?(self) end.flatten.compact end |
#weight=(weight) ⇒ Bignum
Sets the weight for the variant.
107 108 109 |
# File 'app/models/spree/variant.rb', line 107 def weight=(weight) self[:weight] = Spree::LocalizedNumber.parse(weight) if weight.present? end |