caboose-cms 0.4.151 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/app/assets/javascripts/caboose/admin_products.js +79 -0
- data/app/assets/javascripts/caboose/application.js +2 -1
- data/app/assets/javascripts/caboose/cart.js +168 -0
- data/app/assets/javascripts/caboose/checkout.js +151 -0
- data/app/assets/javascripts/caboose/checkout_module.js +312 -0
- data/app/assets/javascripts/caboose/checkout_step1.js +179 -0
- data/app/assets/javascripts/caboose/checkout_step2.js +39 -0
- data/app/assets/javascripts/caboose/checkout_step3.js +34 -0
- data/app/assets/javascripts/caboose/checkout_step4.js +97 -0
- data/app/assets/javascripts/caboose/main.js +99 -8
- data/app/assets/javascripts/caboose/product.js +284 -0
- data/app/assets/stylesheets/caboose/admin_products.css +86 -0
- data/app/assets/templates/caboose/cart/add_to_cart.jst.ejs +7 -0
- data/app/assets/templates/caboose/cart/line_items.jst.ejs +41 -0
- data/app/assets/templates/caboose/checkout/address.jst.ejs +53 -0
- data/app/assets/templates/caboose/checkout/forms/guest.jst.ejs +8 -0
- data/app/assets/templates/caboose/checkout/forms/register.jst.ejs +11 -0
- data/app/assets/templates/caboose/checkout/forms/signin.jst.ejs +7 -0
- data/app/assets/templates/caboose/checkout/line_items.jst.ejs +31 -0
- data/app/assets/templates/caboose/checkout/login.jst.ejs +21 -0
- data/app/assets/templates/caboose/checkout/payment.jst.ejs +5 -0
- data/app/assets/templates/caboose/checkout/shipping.jst.ejs +18 -0
- data/app/assets/templates/caboose/product/images.jst.ejs +8 -0
- data/app/assets/templates/caboose/product/options.jst.ejs +19 -0
- data/app/controllers/caboose/application_controller.rb +29 -1
- data/app/controllers/caboose/cart_controller.rb +52 -0
- data/app/controllers/caboose/categories_controller.rb +108 -0
- data/app/controllers/caboose/checkout_controller.rb +325 -0
- data/app/controllers/caboose/orders_controller.rb +439 -0
- data/app/controllers/caboose/product_images_controller.rb +38 -0
- data/app/controllers/caboose/products_controller.rb +737 -0
- data/app/controllers/caboose/reviews_controller.rb +15 -0
- data/app/controllers/caboose/variants_controller.rb +218 -0
- data/app/controllers/caboose/vendors_controller.rb +73 -0
- data/app/helpers/caboose/application_helper.rb +4 -0
- data/app/helpers/caboose/categories_helper.rb +82 -0
- data/app/helpers/caboose/checkout_helper.rb +20 -0
- data/app/helpers/caboose/products_helper.rb +8 -0
- data/app/mailers/caboose/orders_mailer.rb +30 -0
- data/app/models/caboose/address.rb +26 -0
- data/app/models/caboose/category.rb +89 -0
- data/app/models/caboose/category_membership.rb +10 -0
- data/app/models/caboose/core_plugin.rb +15 -37
- data/app/models/caboose/customization_membership.rb +10 -0
- data/app/models/caboose/discount.rb +16 -0
- data/app/models/caboose/line_item.rb +81 -0
- data/app/models/caboose/message.rb +20 -0
- data/app/models/caboose/order.rb +191 -0
- data/app/models/caboose/order_discount.rb +10 -0
- data/app/models/caboose/order_pdf.rb +78 -0
- data/app/models/caboose/payment_processors/authorizenet.rb +53 -0
- data/app/models/caboose/payment_processors/base.rb +39 -0
- data/app/models/caboose/payment_processors/payscape.rb +94 -0
- data/app/models/caboose/product.rb +145 -0
- data/app/models/caboose/product_image.rb +62 -0
- data/app/models/caboose/product_image_variant.rb +10 -0
- data/app/models/caboose/review.rb +13 -0
- data/app/models/caboose/schema.rb +205 -1
- data/app/models/caboose/search_filter.rb +191 -0
- data/app/models/caboose/shipping_calculator.rb +81 -0
- data/app/models/caboose/states.rb +61 -52
- data/app/models/caboose/tax_calculator.rb +23 -0
- data/app/models/caboose/tax_line.rb +9 -0
- data/app/models/caboose/variant.rb +99 -0
- data/app/models/caboose/vendor.rb +22 -0
- data/app/views/caboose/cart/index.html.erb +8 -0
- data/app/views/caboose/categories/admin_edit.html.erb +79 -0
- data/app/views/caboose/categories/admin_index.html.erb +11 -0
- data/app/views/caboose/categories/admin_new.html.erb +62 -0
- data/app/views/caboose/checkout/_address_form.html.erb +111 -0
- data/app/views/caboose/checkout/_billing_form.html.erb +47 -0
- data/app/views/caboose/checkout/_cart.html.erb +52 -0
- data/app/views/caboose/checkout/_confirm.html.erb +61 -0
- data/app/views/caboose/checkout/_order_discount.html.erb +40 -0
- data/app/views/caboose/checkout/_shipping_address.html.erb +10 -0
- data/app/views/caboose/checkout/_shipping_method.html.erb +2 -0
- data/app/views/caboose/checkout/_shipping_method_form.html.erb +21 -0
- data/app/views/caboose/checkout/billing.html.erb +11 -0
- data/app/views/caboose/checkout/discount.html.erb +11 -0
- data/app/views/caboose/checkout/empty.html.erb +2 -0
- data/app/views/caboose/checkout/error.html.erb +2 -0
- data/app/views/caboose/checkout/index.html.erb +43 -0
- data/app/views/caboose/checkout/login.html.erb +2 -0
- data/app/views/caboose/checkout/payment.html.erb +79 -0
- data/app/views/caboose/checkout/relay.html.erb +23 -0
- data/app/views/caboose/checkout/relay_old.html.erb +12 -0
- data/app/views/caboose/checkout/relay_postMessage.html.erb +19 -0
- data/app/views/caboose/checkout/shipping.html.erb +15 -0
- data/app/views/caboose/checkout/step_four.html.erb +93 -0
- data/app/views/caboose/checkout/step_one.html.erb +56 -0
- data/app/views/caboose/checkout/step_one_old.html.erb +13 -0
- data/app/views/caboose/checkout/step_three.html.erb +23 -0
- data/app/views/caboose/checkout/step_two.html.erb +52 -0
- data/app/views/caboose/checkout/step_two_old.html.erb +14 -0
- data/app/views/caboose/checkout/thanks.html.erb +5 -0
- data/app/views/caboose/orders/_admin_footer.html.erb +2 -0
- data/app/views/caboose/orders/_admin_header.html.erb +31 -0
- data/app/views/caboose/orders/_quickbooks_order.html.erb +0 -0
- data/app/views/caboose/orders/admin_delete_form.html.erb +21 -0
- data/app/views/caboose/orders/admin_edit.html.erb +271 -0
- data/app/views/caboose/orders/admin_index.html.erb +89 -0
- data/app/views/caboose/orders/admin_new.html.erb +42 -0
- data/app/views/caboose/orders/admin_print.html.erb +72 -0
- data/app/views/caboose/orders_mailer/customer_new_order.html.erb +1 -0
- data/app/views/caboose/orders_mailer/customer_status_updated.html.erb +1 -0
- data/app/views/caboose/orders_mailer/fulfillment_new_order.html.erb +1 -0
- data/app/views/caboose/orders_mailer/shipping_order_ready.html.erb +1 -0
- data/app/views/caboose/products/_admin_footer.html.erb +2 -0
- data/app/views/caboose/products/_admin_header.html.erb +32 -0
- data/app/views/caboose/products/_sort_options.html.erb +19 -0
- data/app/views/caboose/products/admin_add_upcs.html.erb +58 -0
- data/app/views/caboose/products/admin_delete_form.html.erb +21 -0
- data/app/views/caboose/products/admin_edit_categories.html.erb +73 -0
- data/app/views/caboose/products/admin_edit_category_images.html.erb +233 -0
- data/app/views/caboose/products/admin_edit_description.html.erb +38 -0
- data/app/views/caboose/products/admin_edit_general.html.erb +104 -0
- data/app/views/caboose/products/admin_edit_images.html.erb +236 -0
- data/app/views/caboose/products/admin_edit_options.html.erb +51 -0
- data/app/views/caboose/products/admin_edit_seo.html.erb +37 -0
- data/app/views/caboose/products/admin_edit_variant_columns.html.erb +75 -0
- data/app/views/caboose/products/admin_edit_variant_sort_order.html.erb +63 -0
- data/app/views/caboose/products/admin_edit_variants.html.erb +171 -0
- data/app/views/caboose/products/admin_edit_variants_single.html.erb +68 -0
- data/app/views/caboose/products/admin_group_variants.html.erb +433 -0
- data/app/views/caboose/products/admin_index.html.erb +95 -0
- data/app/views/caboose/products/admin_new.html.erb +41 -0
- data/app/views/caboose/products/admin_sort.html copy.erb +155 -0
- data/app/views/caboose/products/admin_sort.html.erb +254 -0
- data/app/views/caboose/products/details.html.erb +438 -0
- data/app/views/caboose/products/index.html.erb +46 -0
- data/app/views/caboose/products/not_available.html.erb +35 -0
- data/app/views/caboose/variants/admin_edit.html.erb +82 -0
- data/app/views/caboose/variants/admin_group.html.erb +184 -0
- data/app/views/caboose/variants/admin_new.html.erb +59 -0
- data/app/views/caboose/vendors/admin_edit.html.erb +24 -0
- data/app/views/caboose/vendors/admin_index.html.erb +30 -0
- data/app/views/caboose/vendors/admin_new.html.erb +34 -0
- data/app/views/layouts/caboose/store/_banner.html.erb +10 -0
- data/app/views/layouts/caboose/store/_banner2.html.erb +10 -0
- data/app/views/layouts/caboose/store/_footer.html.erb +55 -0
- data/app/views/layouts/caboose/store/_header.html.erb +69 -0
- data/app/views/layouts/caboose/store/_sidebar.html.erb +27 -0
- data/app/views/layouts/caboose/store/application.html.erb +33 -0
- data/app/views/layouts/caboose/store/authorize_net.erb +18 -0
- data/app/views/layouts/caboose/store/layout_about.html.erb +42 -0
- data/app/views/layouts/caboose/store/layout_blog.html.erb +159 -0
- data/app/views/layouts/caboose/store/layout_confirm.html.erb +85 -0
- data/app/views/layouts/caboose/store/layout_contact.html.erb +38 -0
- data/app/views/layouts/caboose/store/layout_default.html.erb +10 -0
- data/app/views/layouts/caboose/store/layout_detail.html.erb +114 -0
- data/app/views/layouts/caboose/store/layout_order.html.erb +77 -0
- data/app/views/layouts/caboose/store/layout_pricing.html.erb +182 -0
- data/app/views/layouts/caboose/store/layout_product.html.erb +110 -0
- data/app/views/layouts/caboose/store/layout_profile.html.erb +55 -0
- data/app/views/layouts/caboose/store/layout_single.html.erb +3 -0
- data/app/views/layouts/caboose/store/layout_testimonial.html.erb +110 -0
- data/app/views/layouts/caboose/store/layout_testing.html.erb +4 -0
- data/config/routes.rb +126 -0
- data/lib/caboose.rb +46 -1
- data/lib/caboose/engine.rb +39 -1
- data/lib/caboose/version.rb +1 -1
- data/lib/tasks/caboose.rake +12 -0
- metadata +151 -4
- data/app/assets/javascripts/caboose/admin_page_edit_content_bak.js +0 -164
- data/app/assets/javascripts/caboose/model/#Untitled-1# +0 -2
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'prawn'
|
2
|
+
|
3
|
+
module Caboose
|
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 => "Quantity" , :align => :center, :valign => :bottom },
|
57
|
+
{ :content => "Subtotal" , :align => :center, :valign => :bottom, :width => 60 }
|
58
|
+
]
|
59
|
+
|
60
|
+
order.line_items.each do |li|
|
61
|
+
tbl << [
|
62
|
+
"#{li.variant.product.title}\n#{li.variant.sku}\n#{li.variant.title}",
|
63
|
+
{ :content => li.tracking_number },
|
64
|
+
{ :content => sprintf("%.2f", li.variant.price) , :align => :right },
|
65
|
+
{ :content => "#{li.quantity}" , :align => :right },
|
66
|
+
{ :content => sprintf("%.2f", li.subtotal) , :align => :right }
|
67
|
+
]
|
68
|
+
end
|
69
|
+
tbl << [{ :content => "Subtotal" , :colspan => 4, :align => :right }, { :content => sprintf("%.2f", order.subtotal ) , :align => :right }]
|
70
|
+
tbl << [{ :content => "Tax" , :colspan => 4, :align => :right }, { :content => sprintf("%.2f", order.tax ? order.tax : 0.0 ) , :align => :right }]
|
71
|
+
tbl << [{ :content => "#{order.shipping_method} Shipping & Handling" , :colspan => 4, :align => :right }, { :content => sprintf("%.2f", order.shipping_and_handling ) , :align => :right }]
|
72
|
+
tbl << [{ :content => "Discount" , :colspan => 4, :align => :right }, { :content => sprintf("%.2f", order.discount ? order.discount : 0.0 ) , :align => :right }]
|
73
|
+
tbl << [{ :content => "Total" , :colspan => 4, :align => :right }, { :content => sprintf("%.2f", order.total ) , :align => :right }]
|
74
|
+
|
75
|
+
table tbl
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Caboose::PaymentProcessors::Authorizenet < Caboose::PaymentProcessors::Base
|
2
|
+
def self.api(root, body, test=false)
|
3
|
+
end
|
4
|
+
|
5
|
+
def self.form_url(order=nil)
|
6
|
+
#if Rails.env == 'development'
|
7
|
+
'https://test.authorize.net/gateway/transact.dll'
|
8
|
+
#else
|
9
|
+
# 'https://secure.authorize.net/gateway/transact.dll'
|
10
|
+
#end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.authorize(order, params)
|
14
|
+
order.update_attribute(:transaction_id, params[:x_trans_id]) if params[:x_trans_id]
|
15
|
+
return params[:x_response_code] == '1'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.void(order)
|
19
|
+
response = AuthorizeNet::SIM::Transaction.new(
|
20
|
+
Caboose::authorize_net_login_id,
|
21
|
+
Caboose::authorize_net_transaction_key,
|
22
|
+
order.total,
|
23
|
+
:transaction_type => 'VOID',
|
24
|
+
:transaction_id => order.transaction_id
|
25
|
+
)
|
26
|
+
|
27
|
+
ap response
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.capture(order)
|
31
|
+
response = AuthorizeNet::SIM::Transaction.new(
|
32
|
+
Caboose::authorize_net_login_id,
|
33
|
+
Caboose::authorize_net_transaction_key,
|
34
|
+
order.total,
|
35
|
+
:transaction_type => 'CAPTURE_ONLY',
|
36
|
+
:transaction_id => order.transaction_id
|
37
|
+
)
|
38
|
+
|
39
|
+
ap response
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.refund(order)
|
43
|
+
response = AuthorizeNet::SIM::Transaction.new(
|
44
|
+
Caboose::authorize_net_login_id,
|
45
|
+
Caboose::authorize_net_transaction_key,
|
46
|
+
order.total,
|
47
|
+
:transaction_type => 'CREDIT',
|
48
|
+
:transaction_id => order.transaction_id
|
49
|
+
)
|
50
|
+
|
51
|
+
ap response
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Caboose::PaymentProcessors::Base
|
2
|
+
|
3
|
+
# Gets a transaction object that obfuscates transaction info.
|
4
|
+
# Included in this info is the relay URL, which should be set to /checkout/payment-relay.
|
5
|
+
def get_authorize_transaction(order)
|
6
|
+
return {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Gets the hidden form and hidden iframe to which the form will be submitted.
|
10
|
+
# Both are shown on the /checkout/billing page.
|
11
|
+
def get_authorize_form(transaction)
|
12
|
+
return ""
|
13
|
+
end
|
14
|
+
|
15
|
+
# Handles the relay during an authorize transaction.
|
16
|
+
# Should save the transaction information if a successful authorize.
|
17
|
+
# Returns the response required to make the processor to redirect to /checkout/payment-receipt.
|
18
|
+
def authorize_relay(params)
|
19
|
+
return ""
|
20
|
+
end
|
21
|
+
|
22
|
+
# Called during the receipt of an authorize transaction.
|
23
|
+
# Returns true of false indicating whether the authorize transaction was successful.
|
24
|
+
def authorized?(params)
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
# Called if authorized? returns false.
|
29
|
+
# Returns the error given by the processor.
|
30
|
+
def authorize_error(params)
|
31
|
+
return ""
|
32
|
+
end
|
33
|
+
|
34
|
+
# Captures funds for the given order.
|
35
|
+
# Returns true or false indicating the success of the transaction.
|
36
|
+
def capture(order)
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "rexml/document"
|
2
|
+
|
3
|
+
class Caboose::PaymentProcessors::Payscape < Caboose::PaymentProcessors::Base
|
4
|
+
def self.api(root, body, test=false)
|
5
|
+
|
6
|
+
# Determine if transaction should be a test
|
7
|
+
body['api-key'] = if test or Rails.env == 'development'
|
8
|
+
'2F822Rw39fx762MaV7Yy86jXGTC7sCDy'
|
9
|
+
else
|
10
|
+
Caboose::api_key
|
11
|
+
end
|
12
|
+
|
13
|
+
ap "API key used: #{body['api-key']}"
|
14
|
+
# ap "AUTH USERNAME: #{Caboose::payscape_username}"
|
15
|
+
# ap "AUTH PASSWORD: #{Caboose::payscape_password}"
|
16
|
+
|
17
|
+
uri = URI.parse('https://secure.payscapegateway.com/api/v2/three-step')
|
18
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
19
|
+
http.use_ssl = true
|
20
|
+
request = Net::HTTP::Post.new(uri.path)
|
21
|
+
request.content_type = 'text/xml'
|
22
|
+
request.body = body.to_xml({:root => root})
|
23
|
+
xml_response = http.request(request)
|
24
|
+
document = REXML::Document.new(xml_response.body.to_s)
|
25
|
+
response = Hash.new
|
26
|
+
|
27
|
+
document.root.elements.each { |element| response[element.name] = element.text }
|
28
|
+
|
29
|
+
ap response
|
30
|
+
|
31
|
+
return response
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.form_url(order)
|
35
|
+
response = self.api 'auth', {
|
36
|
+
'redirect-url' => "#{Caboose::store_url}/checkout/relay/#{order.id}",
|
37
|
+
'amount' => order.total.to_s,
|
38
|
+
'billing' => {
|
39
|
+
'first-name' => order.billing_address.first_name,
|
40
|
+
'last-name' => order.billing_address.last_name,
|
41
|
+
'address1' => order.billing_address.address1,
|
42
|
+
'address2' => order.billing_address.address2,
|
43
|
+
'city' => order.billing_address.city,
|
44
|
+
'state' => order.billing_address.state,
|
45
|
+
'postal' => order.billing_address.zip
|
46
|
+
}
|
47
|
+
}, order.test?
|
48
|
+
|
49
|
+
order.transaction_id = response['transaction-id']
|
50
|
+
order.transaction_service = 'payscape'
|
51
|
+
order.save
|
52
|
+
|
53
|
+
return response['form-url']
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.authorized?(order)
|
57
|
+
uri = URI.parse("https://secure.payscapegateway.com/api/query.php?username=#{Caboose::payscape_username}&password=#{Caboose::payscape_password}&transaction_id=#{order.transaction_id}")
|
58
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
59
|
+
http.use_ssl = true
|
60
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
61
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
62
|
+
response = http.request(request)
|
63
|
+
document = Nokogiri::XML response.body
|
64
|
+
actions = Hash.new
|
65
|
+
|
66
|
+
document.xpath('//action').each { |action| actions[action.xpath('action_type').text] = action.xpath('success').text == '1' }
|
67
|
+
return actions['auth']
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.authorize(order, params)
|
71
|
+
response = self.api 'complete-action', { 'token-id' => params['token-id'] }, order.test?
|
72
|
+
order.update_attribute(:transaction_id, params['transaction-id']) if params['transaction-id']
|
73
|
+
return response['result-code'].to_i == 100
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.void(order)
|
77
|
+
response = self.api 'void', { 'transaction-id' => order.transaction_id }, order.test?
|
78
|
+
return response['result-code'].to_i == 100
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.capture(order)
|
82
|
+
response = self.api 'capture', { 'transaction-id' => order.transaction_id }, order.test?
|
83
|
+
return response['result-code'].to_i == 100
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.refund(order)
|
87
|
+
response = self.api 'refund', {
|
88
|
+
'transaction-id' => order.transaction_id,
|
89
|
+
'amount' => order.total.to_s
|
90
|
+
}, order.test?
|
91
|
+
|
92
|
+
return response['result-code'].to_i == 100
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Caboose
|
2
|
+
class Product < ActiveRecord::Base
|
3
|
+
self.table_name = 'store_products'
|
4
|
+
self.primary_key = 'id'
|
5
|
+
|
6
|
+
belongs_to :vendor, :class_name => 'Caboose::Vendor'
|
7
|
+
has_many :customizations, :class_name => 'Caboose::Product', :through => :customization_memberships
|
8
|
+
has_many :customization_memberships, :class_name => 'Caboose::CustomizationMembership'
|
9
|
+
has_many :categories, :class_name => 'Caboose::Category', :through => :category_memberships
|
10
|
+
has_many :category_memberships, :class_name => 'Caboose::CategoryMembership'
|
11
|
+
has_many :variants, :class_name => 'Caboose::Variant', :order => 'option1_sort_order, option2_sort_order, option3_sort_order'
|
12
|
+
has_many :product_images, :class_name => 'Caboose::ProductImage'
|
13
|
+
has_many :proudct_inputs, :class_name => 'Caboose::ProductInput'
|
14
|
+
has_many :reviews, :class_name => 'Caboose::Review'
|
15
|
+
|
16
|
+
#default_scope order('store_products.sort_order')
|
17
|
+
|
18
|
+
attr_accessible :id,
|
19
|
+
:alternate_id,
|
20
|
+
:title,
|
21
|
+
:description,
|
22
|
+
:vendor_id,
|
23
|
+
:handle,
|
24
|
+
:date_available,
|
25
|
+
:seo_title,
|
26
|
+
:seo_description,
|
27
|
+
:option1,
|
28
|
+
:option2,
|
29
|
+
:option3,
|
30
|
+
:default1,
|
31
|
+
:default2,
|
32
|
+
:default3,
|
33
|
+
:status,
|
34
|
+
:custom_input,
|
35
|
+
:featured
|
36
|
+
|
37
|
+
|
38
|
+
#
|
39
|
+
# Scopes
|
40
|
+
#
|
41
|
+
|
42
|
+
scope :featured, -> { where(:featured => true) }
|
43
|
+
|
44
|
+
#
|
45
|
+
# Class Methods
|
46
|
+
#
|
47
|
+
|
48
|
+
def self.with_images
|
49
|
+
joins(:product_images)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.active
|
53
|
+
where(:status => 'Active')
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.by_title
|
57
|
+
order('title')
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Instance Methods
|
62
|
+
#
|
63
|
+
|
64
|
+
def as_json(options={})
|
65
|
+
self.attributes.merge({
|
66
|
+
:variants => self.variants,
|
67
|
+
:images => self.product_images
|
68
|
+
})
|
69
|
+
end
|
70
|
+
|
71
|
+
def options
|
72
|
+
options = []
|
73
|
+
|
74
|
+
options << self.option1 if !self.option1.nil? && self.option1.strip.length > 0
|
75
|
+
options << self.option2 if !self.option2.nil? && self.option2.strip.length > 0
|
76
|
+
options << self.option3 if !self.option3.nil? && self.option3.strip.length > 0
|
77
|
+
|
78
|
+
return options
|
79
|
+
end
|
80
|
+
|
81
|
+
def most_popular_variant
|
82
|
+
self.variants.where('price > ? AND status != ?', 0, 'Deleted').order('price ASC').first
|
83
|
+
end
|
84
|
+
|
85
|
+
def featured_image
|
86
|
+
self.product_images.reject{|p| p.nil?}.first
|
87
|
+
end
|
88
|
+
|
89
|
+
def price_varies
|
90
|
+
arr = variants.collect{ |v| v.price }.uniq
|
91
|
+
return arr.count > 0
|
92
|
+
end
|
93
|
+
|
94
|
+
def price_range
|
95
|
+
min = 100000
|
96
|
+
max = 0
|
97
|
+
|
98
|
+
self.variants.each do |variant|
|
99
|
+
next if variant.nil? or variant.price.nil? or variant.price <= 0
|
100
|
+
min = variant.price if variant.price < min
|
101
|
+
max = variant.price if variant.price > max
|
102
|
+
end
|
103
|
+
|
104
|
+
return [min, max]
|
105
|
+
end
|
106
|
+
|
107
|
+
def url
|
108
|
+
"/products/#{self.id}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def related_items
|
112
|
+
Array.new # TODO should be obvious
|
113
|
+
end
|
114
|
+
|
115
|
+
def in_stock
|
116
|
+
Variant.where(:product_id => self.id).where('quantity_in_stock > 0').count > 0
|
117
|
+
end
|
118
|
+
|
119
|
+
def input_required?
|
120
|
+
!self.custom_input.nil?
|
121
|
+
end
|
122
|
+
|
123
|
+
def active_customizations
|
124
|
+
self.customizations.where(:status => 'Active')
|
125
|
+
end
|
126
|
+
|
127
|
+
def live_variants
|
128
|
+
|
129
|
+
# Return variants that haven't been "deleted"
|
130
|
+
self.variants.where(:status => ['Active', 'Inactive'])
|
131
|
+
end
|
132
|
+
|
133
|
+
def option1_values
|
134
|
+
self.variants.where(:status => 'Active').reorder(:option1_sort_order).pluck(:option1).uniq.reject { |x| x.nil? || x.empty? }
|
135
|
+
end
|
136
|
+
|
137
|
+
def option2_values
|
138
|
+
self.variants.where(:status => 'Active').reorder(:option2_sort_order).pluck(:option2).uniq.reject { |x| x.nil? || x.empty? }
|
139
|
+
end
|
140
|
+
|
141
|
+
def option3_values
|
142
|
+
self.variants.where(:status => 'Active').reorder(:option3_sort_order).pluck(:option3).uniq.reject { |x| x.nil? || x.empty? }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Caboose
|
2
|
+
class ProductImage < ActiveRecord::Base
|
3
|
+
self.table_name = 'store_product_images'
|
4
|
+
|
5
|
+
belongs_to :product
|
6
|
+
has_many :product_image_variants
|
7
|
+
has_many :variants, :through => :product_image_variants
|
8
|
+
|
9
|
+
|
10
|
+
attr_accessible :id,
|
11
|
+
:product_id,
|
12
|
+
:title,
|
13
|
+
:position,
|
14
|
+
:image_file_name,
|
15
|
+
:image_content_type,
|
16
|
+
:image_file_size,
|
17
|
+
:image_updated_at,
|
18
|
+
:square_offset_x,
|
19
|
+
:square_offset_y,
|
20
|
+
:square_scale_factor
|
21
|
+
|
22
|
+
has_attached_file :image,
|
23
|
+
:path => "products/:product_id_:id_:style.:extension",
|
24
|
+
:default_url => '/products/:id_:style.:extension',
|
25
|
+
:s3_protocol => :https,
|
26
|
+
:styles => {
|
27
|
+
tiny: '100x100>',
|
28
|
+
thumb: '250x250>',
|
29
|
+
medium: '400x400>',
|
30
|
+
large: '800x800>',
|
31
|
+
huge: '1200x1200>'
|
32
|
+
}
|
33
|
+
|
34
|
+
default_scope order('position ASC')
|
35
|
+
|
36
|
+
validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png"]
|
37
|
+
|
38
|
+
def process
|
39
|
+
config = AssetSync.config
|
40
|
+
bucket = config.fog_directory
|
41
|
+
uri = "http://#{bucket}.s3.amazonaws.com/media-images/#{self.id}#{File.extname(self.title.downcase)}"
|
42
|
+
self.image = URI.parse(uri)
|
43
|
+
self.save
|
44
|
+
end
|
45
|
+
|
46
|
+
def url(size) # 'tiny', 'thumb', 'medium', 'large', 'huge'
|
47
|
+
self.image.url(size)
|
48
|
+
end
|
49
|
+
|
50
|
+
def as_json(options={})
|
51
|
+
self.attributes.merge({
|
52
|
+
:urls => {
|
53
|
+
:tiny => self.url(:tiny),
|
54
|
+
:thumb => self.url(:thumb),
|
55
|
+
:medium => self.url(:medium),
|
56
|
+
:large => self.url(:large),
|
57
|
+
:huge => self.url(:huge)
|
58
|
+
}
|
59
|
+
})
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|