caboose-store 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|