Class: Spree::Variant
- Inherits:
-
Object
- Object
- Spree::Variant
- Includes:
- PgSearch::Model, DefaultPrice, MemoizedData, Metadata, Webhooks
- Defined in:
- app/models/spree/variant.rb,
app/models/spree/variant/webhooks.rb
Defined Under Namespace
Modules: Webhooks
Constant Summary collapse
- MEMOIZED_METHODS =
%w(purchasable in_stock on_sale backorderable tax_category options_text compare_at_price)
- DIMENSION_UNITS =
%w[mm cm in ft]
- WEIGHT_UNITS =
%w[g kg lb oz]
- LOCALIZED_NUMBERS =
FIXME: cost price should be represented with DisplayMoney class
%w(cost_price weight depth width height)
Class Method Summary collapse
Instance Method Summary collapse
-
#additional_images ⇒ Array<Spree::Image>
Returns additional Images for Variant.
- #amount_in(currency) ⇒ Object
- #available? ⇒ Boolean
- #backorderable? ⇒ Boolean (also: #is_backorderable?)
- #backordered? ⇒ Boolean
- #clear_in_stock_cache ⇒ Object
- #compare_at_amount_in(currency) ⇒ Object
- #compare_at_price ⇒ Object
-
#default_image ⇒ Spree::Image
Returns default Image for Variant.
-
#deleted? ⇒ Boolean
use deleted? rather than checking the attribute directly.
- #descriptive_name ⇒ Object
-
#digital? ⇒ Boolean
Is this variant purely digital? (no physical product).
- #dimension ⇒ Object
- #discontinue! ⇒ Object
- #discontinued? ⇒ Boolean
-
#exchange_name ⇒ Object
Default to master name.
- #find_option_value(opt_name) ⇒ Object
- #human_name ⇒ Object
- #in_stock? ⇒ Boolean
- #in_stock_or_backorderable? ⇒ Boolean
- #name_and_sku ⇒ Object
- #on_sale?(currency) ⇒ Boolean
- #option_value(option_type) ⇒ Object
-
#options ⇒ Array<Hash>
Returns an array of hashes with the option type name, value and presentation.
- #options=(options = {}) ⇒ Object
- #options_text ⇒ Object
- #price_in(currency) ⇒ Object
- #price_modifier_amount(options = {}) ⇒ Object
- #price_modifier_amount_in(currency, options = {}) ⇒ Object
- #purchasable? ⇒ Boolean
-
#secondary_image ⇒ Spree::Image
Returns secondary Image for Variant.
- #set_option_value(opt_name, opt_value, opt_type_position = nil) ⇒ Object
-
#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.
- #sku_and_options_text ⇒ Object
-
#tax_category ⇒ Spree::TaxCategory
Returns tax category for Variant.
-
#tax_category_id ⇒ Integer
Returns tax category ID for Variant.
- #volume ⇒ Object
-
#weight_unit ⇒ String
Returns the weight unit for the variant.
- #with_digital_assets? ⇒ Boolean
Class Method Details
.product_name_or_sku_cont(query) ⇒ Object
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'app/models/spree/variant.rb', line 188 def self.product_name_or_sku_cont(query) sanitized_query = ActiveRecord::Base.sanitize_sql_like(query.to_s.downcase.strip) query_pattern = "%#{sanitized_query}%" sku_condition = arel_table[:sku].lower.matches(query_pattern) if Spree.use_translations? translation_arel_table = Product::Translation.arel_table.alias(Product.translation_table_alias)[:name] product_name_condition = translation_arel_table.lower.matches(query_pattern) joins(:product). join_translation_table(Product). where(product_name_condition.or(sku_condition)) else product_name_condition = Product.arel_table[:name].lower.matches(query_pattern) joins(:product).where(product_name_condition.or(sku_condition)) end end |
.search_by_product_name_or_sku(query) ⇒ Object
206 207 208 |
# File 'app/models/spree/variant.rb', line 206 def self.search_by_product_name_or_sku(query) product_name_or_sku_cont(query) end |
Instance Method Details
#additional_images ⇒ Array<Spree::Image>
Returns additional Images for Variant
288 289 290 |
# File 'app/models/spree/variant.rb', line 288 def additional_images @additional_images ||= (images + product.images).uniq.find_all { |image| image.id != default_image&.id } end |
#amount_in(currency) ⇒ Object
388 389 390 |
# File 'app/models/spree/variant.rb', line 388 def amount_in(currency) price_in(currency).try(:amount) end |
#available? ⇒ Boolean
218 219 220 |
# File 'app/models/spree/variant.rb', line 218 def available? !discontinued? && product.available? end |
#backorderable? ⇒ Boolean Also known as: is_backorderable?
444 445 446 447 448 |
# File 'app/models/spree/variant.rb', line 444 def backorderable? @backorderable ||= Rails.cache.fetch(['variant-backorderable', cache_key_with_version]) do quantifier.backorderable? end end |
#backordered? ⇒ Boolean
490 491 492 |
# File 'app/models/spree/variant.rb', line 490 def backordered? @backordered ||= !in_stock? && stock_items.exists?(backorderable: true) end |
#clear_in_stock_cache ⇒ Object
505 506 507 |
# File 'app/models/spree/variant.rb', line 505 def clear_in_stock_cache Rails.cache.delete(in_stock_cache_key) end |
#compare_at_amount_in(currency) ⇒ Object
392 393 394 |
# File 'app/models/spree/variant.rb', line 392 def compare_at_amount_in(currency) price_in(currency).try(:compare_at_amount) end |
#compare_at_price ⇒ Object
422 423 424 |
# File 'app/models/spree/variant.rb', line 422 def compare_at_price @compare_at_price ||= price_in(cost_currency).try(:compare_at_amount) end |
#default_image ⇒ Spree::Image
Returns default Image for Variant
268 269 270 271 272 273 274 |
# File 'app/models/spree/variant.rb', line 268 def default_image @default_image ||= if images.any? images.first else product.default_image end 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.
262 263 264 |
# File 'app/models/spree/variant.rb', line 262 def deleted? !!deleted_at end |
#descriptive_name ⇒ Object
255 256 257 |
# File 'app/models/spree/variant.rb', line 255 def descriptive_name is_master? ? name + ' - Master' : name + ' - ' + end |
#digital? ⇒ Boolean
Is this variant purely digital? (no physical product)
497 498 499 |
# File 'app/models/spree/variant.rb', line 497 def digital? product.digital? end |
#dimension ⇒ Object
472 473 474 |
# File 'app/models/spree/variant.rb', line 472 def dimension (width || 0) + (height || 0) + (depth || 0) end |
#discontinue! ⇒ Object
482 483 484 |
# File 'app/models/spree/variant.rb', line 482 def discontinue! update_attribute(:discontinue_on, Time.current) end |
#discontinued? ⇒ Boolean
486 487 488 |
# File 'app/models/spree/variant.rb', line 486 def discontinued? !!discontinue_on && discontinue_on <= Time.current end |
#exchange_name ⇒ Object
Default to master name
251 252 253 |
# File 'app/models/spree/variant.rb', line 251 def exchange_name is_master? ? name : end |
#find_option_value(opt_name) ⇒ Object
352 353 354 |
# File 'app/models/spree/variant.rb', line 352 def find_option_value(opt_name) option_values.includes(:option_type).detect { |o| o.option_type.name.parameterize == opt_name.parameterize } end |
#human_name ⇒ Object
210 211 212 213 214 215 216 |
# File 'app/models/spree/variant.rb', line 210 def human_name @human_name ||= option_values. joins(option_type: :product_option_types). merge(product.product_option_types). reorder('spree_product_option_types.position'). pluck(:presentation).join('/') end |
#in_stock? ⇒ Boolean
434 435 436 437 438 439 440 441 442 |
# File 'app/models/spree/variant.rb', line 434 def in_stock? @in_stock ||= if association(:stock_items).loaded? && association(:stock_locations).loaded? total_on_hand.positive? else Rails.cache.fetch(in_stock_cache_key, version: cache_version) do total_on_hand.positive? end end end |
#in_stock_or_backorderable? ⇒ Boolean
222 223 224 |
# File 'app/models/spree/variant.rb', line 222 def in_stock_or_backorderable? self.class.in_stock_or_backorderable.exists?(id: id) end |
#name_and_sku ⇒ Object
426 427 428 |
# File 'app/models/spree/variant.rb', line 426 def name_and_sku "#{name} - #{sku}" end |
#on_sale?(currency) ⇒ Boolean
450 451 452 |
# File 'app/models/spree/variant.rb', line 450 def on_sale?(currency) @on_sale ||= price_in(currency)&.discounted? end |
#option_value(option_type) ⇒ Object
356 357 358 359 360 361 362 |
# File 'app/models/spree/variant.rb', line 356 def option_value(option_type) if option_type.is_a?(Spree::OptionType) option_values.detect { |o| o.option_type_id == option_type.id }.try(:presentation) else find_option_value(option_type).try(:presentation) end end |
#options ⇒ Array<Hash>
Returns an array of hashes with the option type name, value and presentation
294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'app/models/spree/variant.rb', line 294 def ||= option_values. includes(option_type: :product_option_types). merge(product.product_option_types). reorder('spree_product_option_types.position'). map do |option_value| { name: option_value.option_type.name, value: option_value.name, presentation: option_value.presentation } end end |
#options=(options = {}) ⇒ Object
308 309 310 311 312 313 314 |
# File 'app/models/spree/variant.rb', line 308 def ( = {}) .each do |option| next if option[:name].blank? || option[:value].blank? set_option_value(option[:name], option[:value], option[:position]) end end |
#options_text ⇒ Object
246 247 248 |
# File 'app/models/spree/variant.rb', line 246 def ||= Spree::Variants::OptionsPresenter.new(self).to_sentence end |
#price_in(currency) ⇒ Object
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
# File 'app/models/spree/variant.rb', line 364 def price_in(currency) currency = currency&.upcase price = if prices.loaded? && prices.any? prices.detect { |p| p.currency == currency } else prices.find_by(currency: currency) end if price.nil? return Spree::Price.new( currency: currency, variant_id: id ) end price rescue TypeError Spree::Price.new( currency: currency, variant_id: id ) end |
#price_modifier_amount(options = {}) ⇒ Object
409 410 411 412 413 414 415 416 417 418 419 420 |
# File 'app/models/spree/variant.rb', line 409 def price_modifier_amount( = {}) return 0 unless .present? .keys.map do |key| m = "#{key}_price_modifier_amount".to_sym if respond_to? m send(m, [key]) else 0 end end.sum end |
#price_modifier_amount_in(currency, options = {}) ⇒ Object
396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'app/models/spree/variant.rb', line 396 def price_modifier_amount_in(currency, = {}) return 0 unless .present? .keys.map do |key| m = "#{key}_price_modifier_amount_in".to_sym if respond_to? m send(m, currency, [key]) else 0 end end.sum end |
#purchasable? ⇒ Boolean
458 459 460 |
# File 'app/models/spree/variant.rb', line 458 def purchasable? @purchasable ||= in_stock? || backorderable? end |
#secondary_image ⇒ Spree::Image
Returns secondary Image for Variant
278 279 280 281 282 283 284 |
# File 'app/models/spree/variant.rb', line 278 def secondary_image @secondary_image ||= if images.size > 1 images.second else product.secondary_image end end |
#set_option_value(opt_name, opt_value, opt_type_position = nil) ⇒ Object
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'app/models/spree/variant.rb', line 316 def set_option_value(opt_name, opt_value, opt_type_position = nil) # no option values on master return if is_master option_type = Spree::OptionType.where(name: opt_name.parameterize).first_or_initialize do |o| o.name = o.presentation = opt_name o.save! end current_value = find_option_value(opt_name) if current_value.nil? # then we have to check to make sure that the product has the option type product_option_type = if (existing_prod_ot = product.product_option_types.find { |ot| ot.option_type_id == option_type.id }) existing_prod_ot else product_option_type = product.product_option_types.new product_option_type.option_type = option_type end product_option_type.position = opt_type_position if opt_type_position product_option_type.save! if product_option_type.new_record? || product_option_type.changed? else return if current_value.name.parameterize == opt_value.parameterize option_values.delete(current_value) end option_value = option_type.option_values.where(name: opt_value.parameterize).first_or_initialize do |o| o.name = o.presentation = opt_value o.save! end option_values << option_value 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
464 465 466 |
# File 'app/models/spree/variant.rb', line 464 def should_track_inventory? track_inventory? && Spree::Config.track_inventory_levels end |
#sku_and_options_text ⇒ Object
430 431 432 |
# File 'app/models/spree/variant.rb', line 430 def "#{sku} #{options_text}".strip end |
#tax_category ⇒ Spree::TaxCategory
Returns tax category for Variant
228 229 230 231 232 233 234 |
# File 'app/models/spree/variant.rb', line 228 def tax_category @tax_category ||= if self[:tax_category_id].nil? product.tax_category else Spree::TaxCategory.find_by(id: self[:tax_category_id]) || product.tax_category end end |
#tax_category_id ⇒ Integer
Returns tax category ID for Variant
238 239 240 241 242 243 244 |
# File 'app/models/spree/variant.rb', line 238 def tax_category_id @tax_category_id ||= if self[:tax_category_id].nil? product.tax_category_id else self[:tax_category_id] end end |
#volume ⇒ Object
468 469 470 |
# File 'app/models/spree/variant.rb', line 468 def volume (width || 0) * (height || 0) * (depth || 0) end |
#weight_unit ⇒ String
Returns the weight unit for the variant
478 479 480 |
# File 'app/models/spree/variant.rb', line 478 def weight_unit attributes['weight_unit'] || Spree::Store.default.preferred_weight_unit end |
#with_digital_assets? ⇒ Boolean
501 502 503 |
# File 'app/models/spree/variant.rb', line 501 def with_digital_assets? digitals.any? end |