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")


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
190
191
192
193
194
# File 'app/models/shoppe/product.rb', line 152

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 = where(name: row["name"]).take
        # Dont import products with the same name but update quantities
        qty = row["qty"].to_i
        if qty > 0
          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.where(name: row["category_name"]).present?
            Shoppe::ProductCategory.where(name: row["category_name"]).take
          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



196
197
198
199
200
201
202
203
# File 'app/models/shoppe/product.rb', line 196

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'])

Returns:

  • (Enumerable)


143
144
145
146
# File 'app/models/shoppe/product.rb', line 143

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

Returns:

  • (String)


132
133
134
# File 'app/models/shoppe/product.rb', line 132

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

#default_imageString

Return attachment for the default_image role

Returns:

  • (String)


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

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

#default_image_file=(file) ⇒ Object

Set attachment for the default_image role



125
126
127
# File 'app/models/shoppe/product.rb', line 125

def default_image_file=(file)
  self.attachments.build(file: file, role: 'default_image')
end

#default_variantShoppe::Product

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

Returns:



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

Returns:

  • (String)


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?

Returns:

  • (Boolean)


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?

Returns:

  • (Boolean)


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?

Returns:

  • (Boolean)


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

Returns:

  • (BigDecimal)


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

Returns:

  • (Fixnum)


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

Returns:



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?

Returns:

  • (Boolean)


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

def variant?
  !self.parent_id.blank?
end