Class: Shoppe::Product

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/shoppe/product.rb,
app/models/shoppe/product/variants.rb,
app/models/shoppe/product/product_attributes.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#product_attributes_arrayObject

Used for setting an array of product attributes which will be updated. Usually received from a web browser.


9
10
11
# File 'app/models/shoppe/product/product_attributes.rb', line 9

def product_attributes_array
  @product_attributes_array
end

Class Method Details

.import(file) ⇒ Object

Imports products from a spreadsheet file Example:

Shoppe:Product.import("path/to/file.csv")

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'app/models/shoppe/product.rb', line 147

def self.import(file)
  spreadsheet = open_spreadsheet(file)
  spreadsheet.default_sheet = spreadsheet.sheets.first
  header = spreadsheet.row(1)
  (2..spreadsheet.last_row).each do |i|
    row = Hash[[header, spreadsheet.row(i)].transpose]

    # Don't import products where the name is blank
    unless row["name"].nil?
      if product = find_by(name: row["name"])
        # Dont import products with the same name but update quantities if they're not the same
        qty = row["qty"].to_i
        if qty > 0 && qty != product.stock
          product.stock_level_adjustments.create!(description: I18n.t('shoppe.import'), adjustment: qty)
        end
      else
        product = new
        product.name = row["name"]
        product.sku = row["sku"]
        product.description = row["description"]
        product.short_description = row["short_description"]
        product.weight = row["weight"]
        product.price = row["price"].nil? ? 0 : row["price"]
        product.permalink  = row["permalink"]

        product.product_categories << begin
          if Shoppe::ProductCategory.find_by(name: row["category_name"]).present?
            Shoppe::ProductCategory.find_by(name: row["category_name"])
          else
            Shoppe::ProductCategory.create(name: row["category_name"])
          end
        end

        product.save!

        qty = row["qty"].to_i
        if qty > 0
          product.stock_level_adjustments.create!(description: I18n.t('shoppe.import'), adjustment: qty)
        end
      end
    end
  end
end

.open_spreadsheet(file) ⇒ Object


191
192
193
194
195
196
197
198
# File 'app/models/shoppe/product.rb', line 191

def self.open_spreadsheet(file)
  case File.extname(file.original_filename)
  when ".csv" then Roo::CSV.new(file.path)
  when ".xls" then Roo::Excel.new(file.path)
  when ".xlsx" then Roo::Excelx.new(file.path)
  else raise I18n.t('shoppe.imports.errors.unknown_format', filename: File.original_filename)
  end
end

.with_attributes(key, values) ⇒ Enumerable

Search for products which include the given attributes and return an active record scope of these products. Chainable with other scopes and with_attributes methods. For example:

Shoppe::Product.active.with_attribute('Manufacturer', 'Apple').with_attribute('Model', ['Macbook', 'iPhone'])

138
139
140
141
# File 'app/models/shoppe/product.rb', line 138

def self.with_attributes(key, values)
  product_ids = Shoppe::ProductAttribute.searchable.where(:key => key, :value => values).pluck(:product_id).uniq
  where(:id => product_ids)
end

Instance Method Details

#attachments=(attrs) ⇒ Object


65
66
67
68
69
70
# File 'app/models/shoppe/product.rb', line 65

def attachments=(attrs)
  if attrs["default_image"]["file"].present? then self.attachments.build(attrs["default_image"]) end
  if attrs["data_sheet"]["file"].present? then self.attachments.build(attrs["data_sheet"]) end

  if attrs["extra"]["file"].present? then attrs["extra"]["file"].each { |attr| self.attachments.build(file: attr, parent_id: attrs["extra"]["parent_id"], parent_type: attrs["extra"]["parent_type"]) } end
end

#data_sheetString

Return attachment for the data_sheet role


127
128
129
# File 'app/models/shoppe/product.rb', line 127

def data_sheet
  self.attachments.for("data_sheet")
end

#default_imageString

Return attachment for the default_image role


120
121
122
# File 'app/models/shoppe/product.rb', line 120

def default_image
  self.attachments.for("default_image")
end

#default_variantShoppe::Product

Returns the default variant for the product or nil if none exists.


38
39
40
41
# File 'app/models/shoppe/product/variants.rb', line 38

def default_variant
  return nil if self.parent
  @default_variant ||= self.variants.select { |v| v.default? }.first
end

#full_nameString

Return the name of the product


75
76
77
# File 'app/models/shoppe/product.rb', line 75

def full_name
  self.parent ? "#{self.parent.name} (#{name})" : name
end

#has_variants?Boolean

Does this product have any variants?


31
32
33
# File 'app/models/shoppe/product/variants.rb', line 31

def has_variants?
  !variants.empty?
end

#in_stock?Boolean

Is this product currently in stock?


99
100
101
# File 'app/models/shoppe/product.rb', line 99

def in_stock?
  self.default_variant ? self.default_variant.in_stock? : (stock_control? ? stock > 0 : true)
end

#orderable?Boolean

Is this product orderable?


82
83
84
85
86
# File 'app/models/shoppe/product.rb', line 82

def orderable?
  return false unless self.active?
  return false if self.has_variants?
  true
end

#priceBigDecimal

The price for the product


91
92
93
94
# File 'app/models/shoppe/product.rb', line 91

def price
  # self.default_variant ? self.default_variant.price : read_attribute(:price)
  self.default_variant ? self.default_variant.price : read_attribute(:price)
end

#product_categoriesShoppe::ProductCategory

The product's categories


23
# File 'app/models/shoppe/product.rb', line 23

has_many :product_categories, class_name: 'Shoppe::ProductCategory', through: :product_categorizations

#product_categorizationsShoppe::ProductCategorization

The product's categorizations


19
# File 'app/models/shoppe/product.rb', line 19

has_many :product_categorizations, dependent: :destroy, class_name: 'Shoppe::ProductCategorization', inverse_of: :product

#product_categoryShoppe::ProductCategory

Return the first product category


113
114
115
# File 'app/models/shoppe/product.rb', line 113

def product_category
  self.product_categories.first rescue nil
end

#stockFixnum

Return the total number of items currently in stock


106
107
108
# File 'app/models/shoppe/product.rb', line 106

def stock
  self.stock_level_adjustments.sum(:adjustment)
end

#tax_rateShoppe::TaxRate

The product's tax rate


28
# File 'app/models/shoppe/product.rb', line 28

belongs_to :tax_rate, :class_name => "Shoppe::TaxRate"

#variant?Boolean

Is this product a variant of another?


46
47
48
# File 'app/models/shoppe/product/variants.rb', line 46

def variant?
  !self.parent_id.blank?
end