caboose-store 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +113 -0
- data/Rakefile +38 -0
- data/app/assets/images/caboose_store/caboose_logo_small.png +0 -0
- data/app/assets/images/caboose_store/caboose_nav.png +0 -0
- data/app/assets/images/caboose_store/caboose_nav_black.png +0 -0
- data/app/assets/images/caboose_store/default_user_pic.png +0 -0
- data/app/assets/images/caboose_store/loading_green.gif +0 -0
- data/app/assets/images/caboose_store/loading_small_white_on_black.gif +0 -0
- data/app/assets/images/caboose_store/loading_white_on_black.gif +0 -0
- data/app/assets/javascripts/caboose_store/admin.js +20 -0
- data/app/assets/javascripts/caboose_store/application.js +17 -0
- data/app/assets/javascripts/caboose_store/modal.js +52 -0
- data/app/assets/javascripts/caboose_store/modal_integration.js +25 -0
- data/app/assets/javascripts/caboose_store/model.form.page.js +30 -0
- data/app/assets/javascripts/caboose_store/model.form.user.js +36 -0
- data/app/assets/javascripts/caboose_store/shortcut.js +11 -0
- data/app/assets/javascripts/caboose_store/station.js +60 -0
- data/app/assets/stylesheets/caboose_store/admin.css +100 -0
- data/app/assets/stylesheets/caboose_store/application.css +19 -0
- data/app/assets/stylesheets/caboose_store/bound_input.css +1 -0
- data/app/assets/stylesheets/caboose_store/caboose.css +4 -0
- data/app/assets/stylesheets/caboose_store/fonts/big_noodle_titling.ttf +0 -0
- data/app/assets/stylesheets/caboose_store/fonts/big_noodle_titling_oblique.ttf +0 -0
- data/app/assets/stylesheets/caboose_store/fonts.css +5 -0
- data/app/assets/stylesheets/caboose_store/login.css +23 -0
- data/app/assets/stylesheets/caboose_store/modal.css +240 -0
- data/app/assets/stylesheets/caboose_store/page_bar_generator.css +34 -0
- data/app/assets/stylesheets/caboose_store/register.css +25 -0
- data/app/assets/stylesheets/caboose_store/station_modal.css +104 -0
- data/app/assets/stylesheets/caboose_store/station_sidebar.css +232 -0
- data/app/assets/stylesheets/caboose_store/tinymce.css +25 -0
- data/app/controllers/caboose_store/application_controller.rb +18 -0
- data/app/controllers/caboose_store/cart_controller.rb +59 -0
- data/app/controllers/caboose_store/categories_controller.rb +128 -0
- data/app/controllers/caboose_store/checkout_controller.rb +164 -0
- data/app/controllers/caboose_store/orders_controller.rb +264 -0
- data/app/controllers/caboose_store/product_images_controller.rb +38 -0
- data/app/controllers/caboose_store/products_controller.rb +387 -0
- data/app/controllers/caboose_store/reviews_controller.rb +15 -0
- data/app/controllers/caboose_store/variants_controller.rb +152 -0
- data/app/helpers/caboose_store/application_helper.rb +46 -0
- data/app/helpers/caboose_store/cart_helper.rb +28 -0
- data/app/helpers/caboose_store/categories_helper.rb +38 -0
- data/app/helpers/caboose_store/products_helper.rb +87 -0
- data/app/mailers/caboose_store/orders_mailer.rb +36 -0
- data/app/models/caboose_store/address.rb +30 -0
- data/app/models/caboose_store/caboose_store_plugin.rb +22 -0
- data/app/models/caboose_store/category.rb +63 -0
- data/app/models/caboose_store/category_membership.rb +11 -0
- data/app/models/caboose_store/discount.rb +14 -0
- data/app/models/caboose_store/message.rb +22 -0
- data/app/models/caboose_store/order.rb +97 -0
- data/app/models/caboose_store/order_discount.rb +11 -0
- data/app/models/caboose_store/order_line_item.rb +13 -0
- data/app/models/caboose_store/order_pdf.rb +82 -0
- data/app/models/caboose_store/product.rb +78 -0
- data/app/models/caboose_store/product_image.rb +25 -0
- data/app/models/caboose_store/product_image_variant.rb +10 -0
- data/app/models/caboose_store/review.rb +13 -0
- data/app/models/caboose_store/schema.rb +146 -0
- data/app/models/caboose_store/shipping_calculator.rb +79 -0
- data/app/models/caboose_store/states.rb +60 -0
- data/app/models/caboose_store/tax_calculator.rb +26 -0
- data/app/models/caboose_store/tax_line.rb +12 -0
- data/app/models/caboose_store/variant.rb +42 -0
- data/app/models/caboose_store/vendor.rb +7 -0
- data/app/views/caboose_store/application/_category_thumb.html.erb +6 -0
- data/app/views/caboose_store/application/_product_thumb.html.erb +13 -0
- data/app/views/caboose_store/cart/index.html.erb +19 -0
- data/app/views/caboose_store/categories/admin_edit.html.erb +82 -0
- data/app/views/caboose_store/categories/admin_index.html.erb +13 -0
- data/app/views/caboose_store/categories/admin_new.html.erb +45 -0
- data/app/views/caboose_store/checkout/billing.html.erb +168 -0
- data/app/views/caboose_store/checkout/discount.html.erb +166 -0
- data/app/views/caboose_store/checkout/index.html.erb +113 -0
- data/app/views/caboose_store/checkout/quantity_box.html.erb +39 -0
- data/app/views/caboose_store/checkout/shipping.html.erb +90 -0
- data/app/views/caboose_store/checkout/thank_you.html.erb +36 -0
- data/app/views/caboose_store/layouts/_banner.html.erb +10 -0
- data/app/views/caboose_store/layouts/_banner2.html.erb +10 -0
- data/app/views/caboose_store/layouts/_footer.html.erb +55 -0
- data/app/views/caboose_store/layouts/_header.html.erb +69 -0
- data/app/views/caboose_store/layouts/_sidebar.html.erb +27 -0
- data/app/views/caboose_store/layouts/application.html.erb +33 -0
- data/app/views/caboose_store/layouts/authorize_net.erb +18 -0
- data/app/views/caboose_store/layouts/layout_about.html.erb +42 -0
- data/app/views/caboose_store/layouts/layout_blog.html.erb +159 -0
- data/app/views/caboose_store/layouts/layout_confirm.html.erb +85 -0
- data/app/views/caboose_store/layouts/layout_contact.html.erb +38 -0
- data/app/views/caboose_store/layouts/layout_default.html.erb +10 -0
- data/app/views/caboose_store/layouts/layout_detail.html.erb +114 -0
- data/app/views/caboose_store/layouts/layout_order.html.erb +77 -0
- data/app/views/caboose_store/layouts/layout_pricing.html.erb +182 -0
- data/app/views/caboose_store/layouts/layout_product.html.erb +110 -0
- data/app/views/caboose_store/layouts/layout_profile.html.erb +55 -0
- data/app/views/caboose_store/layouts/layout_single.html.erb +3 -0
- data/app/views/caboose_store/layouts/layout_testimonial.html.erb +110 -0
- data/app/views/caboose_store/layouts/layout_testing.html.erb +4 -0
- data/app/views/caboose_store/orders/_admin_footer.html.erb +2 -0
- data/app/views/caboose_store/orders/_admin_header.html.erb +31 -0
- data/app/views/caboose_store/orders/_quickbooks_order.html.erb +0 -0
- data/app/views/caboose_store/orders/admin_delete_form.html.erb +21 -0
- data/app/views/caboose_store/orders/admin_edit.html.erb +173 -0
- data/app/views/caboose_store/orders/admin_index.html.erb +79 -0
- data/app/views/caboose_store/orders/admin_new.html.erb +42 -0
- data/app/views/caboose_store/orders/admin_print.html.erb +72 -0
- data/app/views/caboose_store/orders_mailer/customer_new_order.html.erb +47 -0
- data/app/views/caboose_store/orders_mailer/customer_status_updated.html.erb +49 -0
- data/app/views/caboose_store/orders_mailer/fulfillment_new_order.html.erb +43 -0
- data/app/views/caboose_store/orders_mailer/shipping_order_ready.html.erb +46 -0
- data/app/views/caboose_store/products/_admin_footer.html.erb +2 -0
- data/app/views/caboose_store/products/_admin_header.html.erb +31 -0
- data/app/views/caboose_store/products/admin_delete_form.html.erb +21 -0
- data/app/views/caboose_store/products/admin_edit_categories.html.erb +73 -0
- data/app/views/caboose_store/products/admin_edit_category_images.html.erb +233 -0
- data/app/views/caboose_store/products/admin_edit_description.html.erb +41 -0
- data/app/views/caboose_store/products/admin_edit_general.html.erb +47 -0
- data/app/views/caboose_store/products/admin_edit_images.html.erb +234 -0
- data/app/views/caboose_store/products/admin_edit_options.html.erb +51 -0
- data/app/views/caboose_store/products/admin_edit_seo.html.erb +37 -0
- data/app/views/caboose_store/products/admin_edit_variant_columns.html.erb +75 -0
- data/app/views/caboose_store/products/admin_edit_variants.html.erb +101 -0
- data/app/views/caboose_store/products/admin_edit_variants_single.html.erb +68 -0
- data/app/views/caboose_store/products/admin_index.html.erb +47 -0
- data/app/views/caboose_store/products/admin_new.html.erb +41 -0
- data/app/views/caboose_store/products/details.html.erb +437 -0
- data/app/views/caboose_store/products/index.html.erb +46 -0
- data/app/views/caboose_store/products/not_available.html.erb +35 -0
- data/app/views/caboose_store/variants/admin_edit.html.erb +80 -0
- data/app/views/caboose_store/variants/admin_new.html.erb +59 -0
- data/config/routes.rb +95 -0
- data/lib/caboose-store/caboose_store_helper.rb +35 -0
- data/lib/caboose-store/engine.rb +8 -0
- data/lib/caboose-store/version.rb +3 -0
- data/lib/caboose-store.rb +9 -0
- data/lib/tasks/caboose-store.rake +17 -0
- data/test/caboose_test.rb +7 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +25 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/test_helper.rb +15 -0
- metadata +260 -0
@@ -0,0 +1,87 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
module ProductsHelper
|
3
|
+
|
4
|
+
def category_options(top_categories, selected_id = nil)
|
5
|
+
arr = []
|
6
|
+
top_categories.each do |cat|
|
7
|
+
category_options_helper(cat, selected_id, arr)
|
8
|
+
end
|
9
|
+
return arr.join("\n")
|
10
|
+
end
|
11
|
+
|
12
|
+
def category_options_helper(cat, selected_id, arr, prefix = "")
|
13
|
+
opt = "<option value='#{cat.id}'"
|
14
|
+
opt << " selected='true'" if !selected_id.nil? && selected_id == cat.id
|
15
|
+
opt << ">#{prefix}#{cat.name}</option>"
|
16
|
+
arr << opt
|
17
|
+
cat.children.each do |cat2|
|
18
|
+
category_options_helper(cat2, selected_id, arr, "#{prefix} - ")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def category_checkboxes(top_categories, selected_ids = nil)
|
23
|
+
str = "<ul>"
|
24
|
+
top_categories.each do |cat|
|
25
|
+
category_checkboxes_helper(cat, selected_ids, str)
|
26
|
+
end
|
27
|
+
str << "</ul>"
|
28
|
+
return str
|
29
|
+
end
|
30
|
+
|
31
|
+
def category_checkboxes_helper(cat, selected_ids, str, prefix = "")
|
32
|
+
str << "<li>"
|
33
|
+
if cat.children && cat.children.count > 0
|
34
|
+
str << "<input type='checkbox' id='cat_#{cat.id}' value='#{cat.id}'"
|
35
|
+
str << " checked='true'" if selected_ids && selected_ids.include?(cat.id)
|
36
|
+
str << "> <label for='#{cat.id}'><h2>#{cat.name}</h2></label>"
|
37
|
+
else
|
38
|
+
str << "<input type='checkbox' id='cat_#{cat.id}' value='#{cat.id}'"
|
39
|
+
str << " checked='true'" if selected_ids && selected_ids.include?(cat.id)
|
40
|
+
str << "> <label for='#{cat.id}'>#{cat.name}</label>"
|
41
|
+
end
|
42
|
+
cat.children.each do |cat2|
|
43
|
+
str << "<ul>"
|
44
|
+
category_checkboxes_helper(cat2, selected_ids, str, "#{prefix} ")
|
45
|
+
str << "</ul>"
|
46
|
+
end
|
47
|
+
str << "</li>"
|
48
|
+
end
|
49
|
+
|
50
|
+
def product_price(p)
|
51
|
+
price = ""
|
52
|
+
if p.price_varies
|
53
|
+
arr = p.price_range
|
54
|
+
min = number_to_currency(arr[0], :precision => 2)
|
55
|
+
max = number_to_currency(arr[1], :precision => 2)
|
56
|
+
if min.eql? max
|
57
|
+
price = "#{min}"
|
58
|
+
else
|
59
|
+
price = "#{min}-#{max}"
|
60
|
+
end
|
61
|
+
else
|
62
|
+
price = p.variants[0].price
|
63
|
+
price = number_to_currency(price, :precision => 2)
|
64
|
+
end
|
65
|
+
return price
|
66
|
+
end
|
67
|
+
|
68
|
+
def breadcrumb(p)
|
69
|
+
return p.category.ancestry.collect{|cat| "<li><a href='/products/#{cat.id}'>#{cat.name}</a></li>"}.join("<span class='divider'>/</span>")
|
70
|
+
end
|
71
|
+
|
72
|
+
#def product_thumb(product, price)
|
73
|
+
# style = square_image(product.featured_image)
|
74
|
+
# price = product.most_popular_variant.price if price.nil?
|
75
|
+
#
|
76
|
+
# str = "<li class='thumb'>"
|
77
|
+
# str << "<a href='/products/#{product.id}'>"
|
78
|
+
# str << "<figure class='thumb-image' style='<%= style %>"></figure>"
|
79
|
+
# str << "<div class="thumb-title"><%= product.title %></div>"
|
80
|
+
# str << " <div class="thumb-price"><%= price %></div>"
|
81
|
+
# str << "</a>"
|
82
|
+
# str << "</li><!-- .thumb -->"
|
83
|
+
#
|
84
|
+
#end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class OrdersMailer < ActionMailer::Base
|
3
|
+
default :from => "orders@tuskwearcollection.com"
|
4
|
+
|
5
|
+
# Sends a confirmation email to the customer about a new order
|
6
|
+
def customer_new_order(order)
|
7
|
+
@order = order
|
8
|
+
mail(:to => order.customer.email, :subject => "Thank you from TuskWear!")
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sends a notification email to the fulfillment dept about a new order
|
12
|
+
def fulfillment_new_order(order)
|
13
|
+
@order = order
|
14
|
+
#config = YAML::load(File.open("#{Rails.root}/config/tuskwear.yml"))
|
15
|
+
#fulfillment_email = config[Rails.env]['order_fulfillment_email']
|
16
|
+
#mail(:to => fulfillment_email, :subject => "New Order")
|
17
|
+
mail(:to => 'info@tuskwearcollection.com', :subject => "New Order")
|
18
|
+
#mail(:to => 'william@nine.is', :subject => "New Order")
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sends a notification email to the shipping dept that an order is ready to be shipped
|
22
|
+
def shipping_order_ready(order)
|
23
|
+
@order = order
|
24
|
+
#config = YAML::load(File.open("#{Rails.root}/config/tuskwear.yml"))
|
25
|
+
#shipping_email = config[Rails.env]['order_shipping_email']
|
26
|
+
#mail(:to => shipping_email, :subject => "Order ready for shipping")
|
27
|
+
mail(:to => 'info@tuskwearcollection.com', :subject => "Order ready for shipping")
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sends a notification email to the customer that the status of the order has been changed
|
31
|
+
def customer_status_updated(order)
|
32
|
+
@order = order
|
33
|
+
mail(:to => order.customer.email, :subject => "TuskWear Order Status Update")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class Address < ActiveRecord::Base
|
3
|
+
self.table_name = "store_addresses"
|
4
|
+
|
5
|
+
attr_accessible :id,
|
6
|
+
:name,
|
7
|
+
:first_name,
|
8
|
+
:last_name,
|
9
|
+
:street,
|
10
|
+
:address1,
|
11
|
+
:address2,
|
12
|
+
:company,
|
13
|
+
:city,
|
14
|
+
:state,
|
15
|
+
:province,
|
16
|
+
:province_code,
|
17
|
+
:zip,
|
18
|
+
:country,
|
19
|
+
:country_code,
|
20
|
+
:phone
|
21
|
+
|
22
|
+
#validates :street, presence: true
|
23
|
+
#validates :address1, presence: true
|
24
|
+
#validates :city, presence: true
|
25
|
+
#validates :zip, numericality: true
|
26
|
+
#validates :phone, uniqueness: true
|
27
|
+
#validates :company, presence: true
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class CabooseStorePlugin < Caboose::CaboosePlugin
|
3
|
+
|
4
|
+
def self.admin_nav(nav, user = nil, page = nil)
|
5
|
+
return nav if user.nil?
|
6
|
+
|
7
|
+
item = {
|
8
|
+
'id' => 'general-store',
|
9
|
+
'text' => 'General Store',
|
10
|
+
'children' => []
|
11
|
+
}
|
12
|
+
|
13
|
+
item['children'] << { 'id' => 'categories', 'href' => '/admin/categories' , 'text' => 'Categories' , 'modal' => false } if user.is_allowed('categories' , 'view')
|
14
|
+
item['children'] << { 'id' => 'products' , 'href' => '/admin/products' , 'text' => 'Products' , 'modal' => false } if user.is_allowed('products' , 'view')
|
15
|
+
item['children'] << { 'id' => 'orders' , 'href' => '/admin/orders' , 'text' => 'Orders' , 'modal' => false } if user.is_allowed('orders' , 'view')
|
16
|
+
|
17
|
+
nav << item
|
18
|
+
return nav
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class Category < ActiveRecord::Base
|
3
|
+
self.table_name = "store_categories"
|
4
|
+
|
5
|
+
belongs_to :parent, :class_name => 'Category', :foreign_key => 'parent_id'
|
6
|
+
has_many :children, :class_name => 'Category', :foreign_key => 'parent_id', :order => 'name'
|
7
|
+
has_many :products, :through => :category_memberships, :order => 'title'
|
8
|
+
has_many :category_memberships
|
9
|
+
has_attached_file :image,
|
10
|
+
:path => "categories/:id_:style.jpg",
|
11
|
+
:default_url => '/categories/:id_:style.jpg',
|
12
|
+
:styles => {
|
13
|
+
:tiny => '100x100>',
|
14
|
+
:thumb => '250x250>',
|
15
|
+
:medium => '400x400>',
|
16
|
+
:large => '800x800>',
|
17
|
+
:huge => '1200x1200>'
|
18
|
+
}
|
19
|
+
|
20
|
+
attr_accessible :id, :parent_id, :name, :url, :slug
|
21
|
+
|
22
|
+
def ancestry
|
23
|
+
return [self] if self.parent.nil?
|
24
|
+
arr = self.parent.ancestry
|
25
|
+
arr << self
|
26
|
+
return arr
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.options(selected_id = nil)
|
30
|
+
arr = []
|
31
|
+
self.where(:parent_id => nil).reorder('name').all.each do |cat|
|
32
|
+
self.options_helper(cat, selected_id, arr)
|
33
|
+
end
|
34
|
+
return arr
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.options_helper(cat, selected_id, arr, prefix = "")
|
38
|
+
arr << {
|
39
|
+
:value => cat.id,
|
40
|
+
:text => "#{prefix}#{cat.name}"
|
41
|
+
}
|
42
|
+
cat.children.each do |cat2|
|
43
|
+
self.options_helper(cat2, selected_id, arr, "#{prefix} - ")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def featured_image(size)
|
48
|
+
if self.products == []
|
49
|
+
return 'http://s3.amazonaws.com/categories/no_image.jpg'
|
50
|
+
end
|
51
|
+
|
52
|
+
return self.products.reject{|p| p.featured_image.nil? || p.nil?}.first.featured_image.url(size) unless self.products.first.product_images == []
|
53
|
+
return self.products.reject{|p| p.featured_image.nil? || p.nil?}.second.featured_image.url(size) unless self.products.second.product_images == []
|
54
|
+
return self.products.reject{|p| p.featured_image.nil? || p.nil?}.last.featured_image.url(size) unless self.products.last.product_images == []
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.get_slug(str)
|
59
|
+
return str.gsub(' ', '-').downcase
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class Discount < ActiveRecord::Base
|
3
|
+
self.table_name = "store_discounts"
|
4
|
+
|
5
|
+
attr_accessible :id,
|
6
|
+
:name, # The name of this discount
|
7
|
+
:code, # The code the customer has to input to apply for this discount
|
8
|
+
:amount_flat, # Amount of the savings flat off the total
|
9
|
+
:amount_percentage, # Amount of savings as a percentage off the total
|
10
|
+
:no_shipping, # Whether or not it's a free shipping discount
|
11
|
+
:no_tax # Whether or not it's a free shipping discount
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class Message
|
3
|
+
include ActiveModel::Validations
|
4
|
+
include ActiveModel::Conversion
|
5
|
+
extend ActiveModel::Naming
|
6
|
+
|
7
|
+
attr_accessor :name, :email, :body
|
8
|
+
|
9
|
+
validates :name, :email, :body, presence: true
|
10
|
+
validates :email, format: { with: %r{.+@.+\..+} }, allow_blank: false
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
attributes.each do |name, value|
|
14
|
+
send("#{name}=", value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def persisted?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class Order < ActiveRecord::Base
|
3
|
+
self.table_name = "store_orders"
|
4
|
+
|
5
|
+
belongs_to :customer, :class_name => 'Caboose::User'
|
6
|
+
belongs_to :shipping_address, :class_name => 'Address'
|
7
|
+
belongs_to :billing_address, :class_name => 'Address'
|
8
|
+
has_many :discounts, :through => :order_discounts
|
9
|
+
has_many :order_discounts
|
10
|
+
has_many :order_line_items
|
11
|
+
|
12
|
+
attr_accessible :id,
|
13
|
+
:order_number, # Customer-set unique number of the order
|
14
|
+
:subtotal,
|
15
|
+
:tax,
|
16
|
+
:shipping_method,
|
17
|
+
:shipping_method_code,
|
18
|
+
:shipping,
|
19
|
+
:discount,
|
20
|
+
:total,
|
21
|
+
:status, # The current order status. One of: cart, pending, backordered, fulfilled
|
22
|
+
:payment_status, # The current payment status. One of: nil, 'pending', 'authorized', 'paid', 'voided', 'refunded'
|
23
|
+
:notes, # The note which is attached to the order.
|
24
|
+
:referring_site, # Contains the url of the referrer that brought the customer to your store
|
25
|
+
:landing_site, # Contains the path of the landing site the customer used. The first page that the customer saw when he/she reached the store.
|
26
|
+
:landing_site_ref, # Looks at the landing site and extracts a reference parameter from it.
|
27
|
+
:cancel_reason, # The reason selected when cancelling the order. One of: 'inventory', 'customer', 'fraud', 'other'
|
28
|
+
:date_created,
|
29
|
+
:date_authorized,
|
30
|
+
:date_captured,
|
31
|
+
:date_cancelled
|
32
|
+
|
33
|
+
def line_items
|
34
|
+
return self.order_line_items
|
35
|
+
end
|
36
|
+
|
37
|
+
def subtotal
|
38
|
+
return self.order_line_items.inject(0.0){ |sum,li| sum + li.subtotal }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Array of any Line Items which have been fulfilled (marked as shipped on the order screen).
|
42
|
+
def fulfilled_line_items
|
43
|
+
return self.order_line_items.where(:status => 'shipped').all
|
44
|
+
end
|
45
|
+
|
46
|
+
# Array of those Line Items which have not yet been fulfilled (marked as shipped on the order screen).
|
47
|
+
def unfulfilled_line_items
|
48
|
+
return self.order_line_items.where('status != ?', 'shipped').all
|
49
|
+
end
|
50
|
+
|
51
|
+
def cancelled
|
52
|
+
return self.status == 'cancelled'
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns true if there is at least one item in the order that requires shipping, and returns false otherwise
|
56
|
+
def requires_shipping
|
57
|
+
requires = false
|
58
|
+
self.order_line_items.each do |li|
|
59
|
+
if li.variant.requires_shipping
|
60
|
+
requires = true
|
61
|
+
break
|
62
|
+
end
|
63
|
+
end
|
64
|
+
return requires
|
65
|
+
end
|
66
|
+
|
67
|
+
def calculate_total
|
68
|
+
self.calculate_discount
|
69
|
+
self.total = self.subtotal + self.tax + self.shipping + self.handling - self.discount
|
70
|
+
return self.total
|
71
|
+
end
|
72
|
+
|
73
|
+
def calculate_discount
|
74
|
+
|
75
|
+
percentage_off = 0.0
|
76
|
+
amount_off = 0.0
|
77
|
+
no_shipping = false
|
78
|
+
no_tax = false
|
79
|
+
|
80
|
+
self.discounts.each do |d|
|
81
|
+
percentage_off = percentage_off + d.amount_percentage
|
82
|
+
amount_off = amount_off + d.amount_flat
|
83
|
+
no_shipping = true if d.no_shipping
|
84
|
+
no_tax = true if d.no_tax
|
85
|
+
end
|
86
|
+
|
87
|
+
x = 0.0
|
88
|
+
x = x + self.subtotal * percentage_off if percentage_off > 0
|
89
|
+
x = x + amount_off if amount_off > 0
|
90
|
+
x = x + self.shipping if no_shipping
|
91
|
+
x = x + self.tax if no_tax
|
92
|
+
|
93
|
+
self.discount = x
|
94
|
+
return self.discount
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class OrderLineItem < ActiveRecord::Base
|
3
|
+
self.table_name = "store_order_line_items"
|
4
|
+
|
5
|
+
belongs_to :variant
|
6
|
+
belongs_to :order
|
7
|
+
attr_accessible :id, :variant_id, :quantity, :quantity_shipped
|
8
|
+
|
9
|
+
def subtotal
|
10
|
+
return self.unit_price * self.quantity
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'prawn'
|
2
|
+
|
3
|
+
module CabooseStore
|
4
|
+
class OrderPdf < Prawn::Document
|
5
|
+
|
6
|
+
attr_accessor :order
|
7
|
+
|
8
|
+
def to_pdf
|
9
|
+
image open("https://dmwwflw4i3miv.cloudfront.net/logo.png"), :position => :center
|
10
|
+
text " "
|
11
|
+
customer_info
|
12
|
+
text " "
|
13
|
+
order_table
|
14
|
+
render
|
15
|
+
end
|
16
|
+
|
17
|
+
def customer_info
|
18
|
+
|
19
|
+
order_info = "Order ##{order.id}\n"
|
20
|
+
order_info << "Status: #{order.status.capitalize}\n"
|
21
|
+
order_info << "Received: #{order.date_created.strftime("%m/%d/%Y")}\n"
|
22
|
+
if order.status == 'shipped' && order.date_shipped
|
23
|
+
order_info << "Shipped: #{order.date_shipped.strftime("%m/%d/%Y")}"
|
24
|
+
end
|
25
|
+
|
26
|
+
c = order.customer
|
27
|
+
billed_to = "#{c.first_name} #{c.last_name}\n#{c.email}\n#{c.phone}"
|
28
|
+
|
29
|
+
sa = order.shipping_address
|
30
|
+
shipped_to = "#{sa.name}\n#{sa.address1}\n"
|
31
|
+
shipped_to << "#{sa.address2}\n" if sa.address2.strip.length > 0
|
32
|
+
shipped_to << "#{sa.city}, #{sa.state} #{sa.zip}"
|
33
|
+
|
34
|
+
tbl = []
|
35
|
+
tbl << [
|
36
|
+
{ :content => "Order Info" , :align => :center, :valign => :bottom, :width => 180 },
|
37
|
+
{ :content => "Billed To" , :align => :center, :valign => :bottom, :width => 180 },
|
38
|
+
{ :content => "Shipped To" , :align => :center, :valign => :bottom, :width => 180 }
|
39
|
+
]
|
40
|
+
tbl << [
|
41
|
+
{ :content => order_info, :valign => :top, :height => 75 },
|
42
|
+
{ :content => billed_to , :valign => :top, :height => 75 },
|
43
|
+
{ :content => shipped_to, :valign => :top, :height => 75 }
|
44
|
+
]
|
45
|
+
table tbl
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def order_table
|
50
|
+
|
51
|
+
tbl = []
|
52
|
+
tbl << [
|
53
|
+
{ :content => "Item" , :align => :center, :valign => :bottom },
|
54
|
+
{ :content => "Tracking Number" , :align => :center, :valign => :bottom },
|
55
|
+
{ :content => "Unit Price" , :align => :center, :valign => :bottom },
|
56
|
+
{ :content => "Qty Ordered" , :align => :center, :valign => :bottom },
|
57
|
+
{ :content => "Qty Shipped" , :align => :center, :valign => :bottom },
|
58
|
+
{ :content => "Subtotal" , :align => :center, :valign => :bottom, :width => 60 }
|
59
|
+
]
|
60
|
+
|
61
|
+
order.line_items.each do |li|
|
62
|
+
tbl << [
|
63
|
+
"#{li.variant.product.title}\n#{li.variant.sku}\n#{li.variant.title}",
|
64
|
+
{ :content => li.tracking_number },
|
65
|
+
{ :content => sprintf("%.2f", li.variant.price) , :align => :right },
|
66
|
+
{ :content => "#{li.quantity}" , :align => :right },
|
67
|
+
{ :content => "#{li.quantity_shipped}" , :align => :right },
|
68
|
+
{ :content => sprintf("%.2f", li.subtotal) , :align => :right }
|
69
|
+
]
|
70
|
+
end
|
71
|
+
tbl << [{ :content => "Subtotal" , :colspan => 5, :align => :right }, { :content => sprintf("%.2f", order.subtotal ) , :align => :right }]
|
72
|
+
tbl << [{ :content => "Tax" , :colspan => 5, :align => :right }, { :content => sprintf("%.2f", order.tax ) , :align => :right }]
|
73
|
+
tbl << [{ :content => "#{order.shipping_method} Shipping & Handling" , :colspan => 5, :align => :right }, { :content => sprintf("%.2f", order.shipping + order.handling) , :align => :right }]
|
74
|
+
tbl << [{ :content => "Discount" , :colspan => 5, :align => :right }, { :content => sprintf("%.2f", order.discount ) , :align => :right }]
|
75
|
+
tbl << [{ :content => "Total" , :colspan => 5, :align => :right }, { :content => sprintf("%.2f", order.total ) , :align => :right }]
|
76
|
+
|
77
|
+
table tbl
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class Product < ActiveRecord::Base
|
3
|
+
self.table_name = "store_products"
|
4
|
+
|
5
|
+
belongs_to :vendor
|
6
|
+
has_many :categories, :through => :category_memberships
|
7
|
+
has_many :category_memberships
|
8
|
+
has_many :variants
|
9
|
+
has_many :product_images
|
10
|
+
has_many :reviews
|
11
|
+
|
12
|
+
attr_accessible :id,
|
13
|
+
:title,
|
14
|
+
:description,
|
15
|
+
:vendor_id,
|
16
|
+
:handle,
|
17
|
+
:seo_title,
|
18
|
+
:seo_description,
|
19
|
+
:option1,
|
20
|
+
:option2,
|
21
|
+
:option3,
|
22
|
+
:default1,
|
23
|
+
:default2,
|
24
|
+
:default3,
|
25
|
+
:status,
|
26
|
+
:alternate_id
|
27
|
+
|
28
|
+
def options
|
29
|
+
arr = []
|
30
|
+
arr << self.option1 if !self.option1.nil? && self.option1.strip.length > 0
|
31
|
+
arr << self.option2 if !self.option2.nil? && self.option2.strip.length > 0
|
32
|
+
arr << self.option3 if !self.option3.nil? && self.option3.strip.length > 0
|
33
|
+
return arr
|
34
|
+
end
|
35
|
+
|
36
|
+
def most_popular_variant
|
37
|
+
self.variants.order('price DESC').first
|
38
|
+
end
|
39
|
+
|
40
|
+
def featured_image
|
41
|
+
self.product_images.reject{|p| p.nil?}.first
|
42
|
+
end
|
43
|
+
|
44
|
+
def price_varies
|
45
|
+
arr = variants.collect{ |v| v.price }.uniq
|
46
|
+
return arr.count > 0
|
47
|
+
end
|
48
|
+
|
49
|
+
def price_range
|
50
|
+
min = 100000
|
51
|
+
max = 0
|
52
|
+
variants.each do |v|
|
53
|
+
next if v.nil? || v.price.nil?
|
54
|
+
min = v.price if v.price < min
|
55
|
+
max = v.price if v.price > max
|
56
|
+
end
|
57
|
+
return [min, max]
|
58
|
+
end
|
59
|
+
|
60
|
+
def url
|
61
|
+
return "/products/#{self.id}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def related_items
|
65
|
+
return []
|
66
|
+
end
|
67
|
+
|
68
|
+
def in_stock
|
69
|
+
return Variant.where(:product_id => self.id).where("quantity_in_stock > 0").count > 0
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns non deleted variants
|
73
|
+
def live_variants
|
74
|
+
return self.variants.where(:status => ['Active', 'Inactive'])
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class ProductImage < ActiveRecord::Base
|
3
|
+
self.table_name = "store_product_images"
|
4
|
+
|
5
|
+
belongs_to :product
|
6
|
+
has_and_belongs_to_many :variants
|
7
|
+
attr_accessible :id, :title
|
8
|
+
has_attached_file :image,
|
9
|
+
#:path => "#{Rails.root}/public/products/:product_id_:id_:style.:extension",
|
10
|
+
:path => "products/:product_id_:id_:style.:extension",
|
11
|
+
:default_url => '/products/:id_:style.:extension',
|
12
|
+
:styles => {
|
13
|
+
:tiny => '100x100>',
|
14
|
+
:thumb => '250x250>',
|
15
|
+
:medium => '400x400>',
|
16
|
+
:large => '800x800>',
|
17
|
+
:huge => '1200x1200>'
|
18
|
+
}
|
19
|
+
|
20
|
+
def url(size) # 'tiny', 'thumb', 'medium', 'large', 'huge'
|
21
|
+
self.image.url(size)
|
22
|
+
#"https://s3.amazonaws.com/tuskwear/products/#{self.product_id}_#{self.id}_#{size}.jpg"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|