caboose-store 0.0.10 → 0.0.11
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/main.js +8 -0
- data/app/assets/javascripts/caboose_store/admin.js +0 -19
- data/app/assets/javascripts/caboose_store/admin_products.js +3 -4
- data/app/assets/javascripts/caboose_store/application.js +6 -5
- data/app/assets/javascripts/caboose_store/cart.js.bak +258 -0
- data/app/assets/javascripts/caboose_store/checkout.js +151 -0
- data/app/assets/javascripts/caboose_store/config.js +83 -0
- data/app/assets/javascripts/caboose_store/jquery-ui-multisortable.js +254 -0
- data/app/assets/javascripts/caboose_store/jquery-ui.js +3572 -0
- data/app/assets/javascripts/caboose_store/main.js +20 -0
- data/app/assets/javascripts/caboose_store/modules/cart.js +167 -0
- data/app/assets/javascripts/caboose_store/modules/checkout.js +313 -0
- data/app/assets/javascripts/caboose_store/modules/product.js +282 -0
- data/app/assets/stylesheets/caboose_store/admin.css +3 -3
- data/app/assets/stylesheets/caboose_store/application.css +2 -2
- data/app/assets/stylesheets/caboose_store/modal.css +1 -0
- data/app/assets/templates/caboose_store/cart/add_to_cart.jst.ejs +7 -0
- data/app/assets/templates/caboose_store/cart/line_items.jst.ejs +41 -0
- data/app/assets/templates/caboose_store/checkout/address.jst.ejs +53 -0
- data/app/assets/templates/caboose_store/checkout/forms/guest.jst.ejs +8 -0
- data/app/assets/templates/caboose_store/checkout/forms/register.jst.ejs +11 -0
- data/app/assets/templates/caboose_store/checkout/forms/signin.jst.ejs +7 -0
- data/app/assets/templates/caboose_store/checkout/line_items.jst.ejs +32 -0
- data/app/assets/templates/caboose_store/checkout/login.jst.ejs +21 -0
- data/app/assets/templates/caboose_store/checkout/payment.jst.ejs +5 -0
- data/app/assets/templates/caboose_store/checkout/shipping.jst.ejs +18 -0
- data/app/assets/templates/caboose_store/product/images.jst.ejs +8 -0
- data/app/assets/templates/caboose_store/product/options.jst.ejs +19 -0
- data/app/controllers/caboose_store/application_controller.rb +1 -11
- data/app/controllers/caboose_store/cart_controller.rb +36 -44
- data/app/controllers/caboose_store/cart_controller.rb.bak +169 -0
- data/app/controllers/caboose_store/categories_controller.rb +72 -92
- data/app/controllers/caboose_store/checkout_controller.rb +231 -151
- data/app/controllers/caboose_store/orders_controller.rb +235 -95
- data/app/controllers/caboose_store/product_images_controller.rb +1 -1
- data/app/controllers/caboose_store/products_controller.rb +275 -84
- data/app/controllers/caboose_store/reviews_controller.rb +1 -1
- data/app/controllers/caboose_store/variants_controller.rb +86 -20
- data/app/controllers/caboose_store/vendors_controller.rb +73 -0
- data/app/helpers/caboose_store/application_helper.rb +3 -41
- data/app/helpers/caboose_store/cart_helper.rb +0 -24
- data/app/helpers/caboose_store/categories_helper.rb +69 -25
- data/app/helpers/caboose_store/checkout_helper.rb +31 -0
- data/app/helpers/caboose_store/products_helper.rb +4 -80
- data/app/mailers/caboose_store/orders_mailer.rb +6 -13
- data/app/models/caboose_store/address.rb +5 -9
- data/app/models/caboose_store/caboose_store_plugin.rb +18 -7
- data/app/models/caboose_store/category.rb +66 -41
- data/app/models/caboose_store/category_membership.rb +1 -2
- data/app/models/caboose_store/customization_membership.rb +10 -0
- data/app/models/caboose_store/discount.rb +4 -2
- data/app/models/caboose_store/line_item.rb +77 -0
- data/app/models/caboose_store/message.rb +3 -5
- data/app/models/caboose_store/order.rb +153 -63
- data/app/models/caboose_store/order_discount.rb +1 -2
- data/app/models/caboose_store/order_pdf.rb +2 -4
- data/app/models/caboose_store/payment_processors/authorizenet.rb +20 -2
- data/app/models/caboose_store/payment_processors/base.rb +0 -2
- data/app/models/caboose_store/payment_processors/payscape.rb +91 -1
- data/app/models/caboose_store/product.rb +86 -30
- data/app/models/caboose_store/product_image.rb +50 -15
- data/app/models/caboose_store/product_image_variant.rb +3 -3
- data/app/models/caboose_store/review.rb +8 -8
- data/app/models/caboose_store/schema.rb +174 -119
- data/app/models/caboose_store/search_filter.rb +110 -115
- data/app/models/caboose_store/shipping_calculator.rb +51 -49
- data/app/models/caboose_store/tax_calculator.rb +2 -6
- data/app/models/caboose_store/tax_line.rb +1 -4
- data/app/models/caboose_store/variant.rb +77 -21
- data/app/models/caboose_store/vendor.rb +17 -2
- data/app/views/caboose_store/cart/index.html.erb +3 -18
- data/app/views/caboose_store/categories/admin_edit.html.erb +64 -67
- data/app/views/caboose_store/categories/admin_index.html.erb +7 -9
- data/app/views/caboose_store/categories/admin_new.html.erb +56 -39
- data/app/views/caboose_store/checkout/_address_form.html.erb +111 -0
- data/app/views/caboose_store/checkout/_billing_form.html.erb +47 -0
- data/app/views/caboose_store/checkout/_cart.html.erb +52 -0
- data/app/views/caboose_store/checkout/_order_discount.html.erb +40 -0
- data/app/views/caboose_store/checkout/_shipping_address.html.erb +10 -0
- data/app/views/caboose_store/checkout/_shipping_method.html.erb +2 -0
- data/app/views/caboose_store/checkout/_shipping_method_form.html.erb +21 -0
- data/app/views/caboose_store/checkout/billing.html.erb +4 -161
- data/app/views/caboose_store/checkout/discount.html.erb +5 -160
- data/app/views/caboose_store/checkout/empty.html.erb +2 -0
- data/app/views/caboose_store/checkout/error.html.erb +2 -0
- data/app/views/caboose_store/checkout/index.html.erb +30 -100
- data/app/views/caboose_store/checkout/login.html.erb +2 -0
- data/app/views/caboose_store/checkout/payment.html.erb +79 -0
- data/app/views/caboose_store/checkout/relay.html.erb +12 -0
- data/app/views/caboose_store/checkout/relay_postMessage.html.erb +19 -0
- data/app/views/caboose_store/checkout/shipping.html.erb +8 -83
- data/app/views/caboose_store/checkout/step_one.html.erb +15 -0
- data/app/views/caboose_store/checkout/step_two.html.erb +15 -0
- data/app/views/caboose_store/checkout/thanks.html.erb +5 -0
- data/app/views/caboose_store/layouts/_footer.html.erb +1 -1
- data/app/views/caboose_store/orders/admin_edit.html.erb +101 -7
- data/app/views/caboose_store/orders/admin_index.html.erb +8 -8
- data/app/views/caboose_store/orders_mailer/customer_new_order.html.erb +0 -46
- data/app/views/caboose_store/orders_mailer/customer_status_updated.html.erb +0 -48
- data/app/views/caboose_store/orders_mailer/fulfillment_new_order.html.erb +0 -42
- data/app/views/caboose_store/orders_mailer/shipping_order_ready.html.erb +0 -45
- data/app/views/caboose_store/products/_admin_header.html.erb +7 -6
- data/app/views/caboose_store/products/_sort_options.html.erb +19 -0
- data/app/views/caboose_store/products/admin_add_upcs.html.erb +58 -0
- data/app/views/caboose_store/products/admin_edit_categories.html.erb +1 -1
- data/app/views/caboose_store/products/admin_edit_description.html.erb +2 -5
- data/app/views/caboose_store/products/admin_edit_general.html.erb +58 -1
- data/app/views/caboose_store/products/admin_edit_images.html.erb +3 -1
- data/app/views/caboose_store/products/admin_edit_variants.html.erb +13 -2
- data/app/views/caboose_store/products/admin_edit_variants_single.html.erb +14 -14
- data/app/views/caboose_store/products/admin_group_variants.html.erb +433 -0
- data/app/views/caboose_store/products/admin_index.html.erb +87 -39
- data/app/views/caboose_store/products/admin_sort.html copy.erb +155 -0
- data/app/views/caboose_store/products/admin_sort.html.erb +254 -0
- data/app/views/caboose_store/products/details.html.erb +1 -2
- data/app/views/caboose_store/variants/admin_edit.html.erb +18 -16
- data/app/views/caboose_store/variants/admin_group.html.erb +184 -0
- data/app/views/caboose_store/vendors/admin_edit.html.erb +24 -0
- data/app/views/caboose_store/vendors/admin_index.html.erb +30 -0
- data/app/views/caboose_store/vendors/admin_new.html.erb +34 -0
- data/config/routes.rb +65 -27
- data/lib/caboose-store/caboose_store_helper.rb +29 -30
- data/lib/caboose-store/engine.rb +73 -28
- data/lib/caboose-store/version.rb +1 -1
- data/lib/caboose-store.rb +16 -7
- data/lib/tasks/caboose-store.rake +0 -2
- metadata +54 -17
- data/app/assets/javascripts/caboose_store/modal.js +0 -52
- data/app/assets/javascripts/caboose_store/modal_integration.js +0 -25
- data/app/assets/javascripts/caboose_store/model.form.page.js +0 -30
- data/app/assets/javascripts/caboose_store/model.form.user.js +0 -36
- data/app/assets/javascripts/caboose_store/payment_processors/authorizenet.js +0 -21
- data/app/assets/javascripts/caboose_store/payment_processors/base.js +0 -21
- data/app/assets/javascripts/caboose_store/payment_processors/payscape.js +0 -21
- data/app/assets/javascripts/caboose_store/shortcut.js +0 -11
- data/app/assets/javascripts/caboose_store/station.js +0 -60
- data/app/models/caboose_store/order_line_item.rb +0 -13
- data/app/models/caboose_store/states.rb +0 -60
- data/app/views/caboose_store/application/_category_thumb.html.erb +0 -6
- data/app/views/caboose_store/application/_product_thumb.html.erb +0 -13
- data/app/views/caboose_store/checkout/quantity_box.html.erb +0 -39
- data/app/views/caboose_store/checkout/thank_you.html.erb +0 -36
@@ -0,0 +1,77 @@
|
|
1
|
+
module CabooseStore
|
2
|
+
class LineItem < ActiveRecord::Base
|
3
|
+
self.table_name = 'store_line_items'
|
4
|
+
|
5
|
+
belongs_to :variant
|
6
|
+
belongs_to :order, :dependent => :destroy
|
7
|
+
belongs_to :parent, :class_name => 'LineItem', :foreign_key => 'parent_id'
|
8
|
+
has_many :children, :class_name => 'LineItem', :foreign_key => 'parent_id'
|
9
|
+
|
10
|
+
attr_accessible :id,
|
11
|
+
:variant_id,
|
12
|
+
:quantity,
|
13
|
+
:price,
|
14
|
+
:notes,
|
15
|
+
:order_id,
|
16
|
+
:status,
|
17
|
+
:tracking_number,
|
18
|
+
:custom1,
|
19
|
+
:custom2,
|
20
|
+
:custom3
|
21
|
+
|
22
|
+
#
|
23
|
+
# Scopes
|
24
|
+
#
|
25
|
+
|
26
|
+
scope :pending, where('status = ?', 'pending')
|
27
|
+
scope :fulfilled, where('status = ?', 'shipped')
|
28
|
+
scope :unfulfilled, where('status != ?', 'shipped')
|
29
|
+
#
|
30
|
+
# Validations
|
31
|
+
#
|
32
|
+
|
33
|
+
validates :status, :inclusion => {
|
34
|
+
:in => ['pending', 'shipped'],
|
35
|
+
:message => "%{value} is not a valid status. Must be either 'pending' or 'shipped'"
|
36
|
+
}
|
37
|
+
|
38
|
+
validates :quantity, :numericality => { :greater_than_or_equal_to => 0 }
|
39
|
+
|
40
|
+
validate :quantity_in_stock
|
41
|
+
def quantity_in_stock
|
42
|
+
errors.add(:base, "There #{self.variant.quantity > 1 ? 'are' : 'is'} only #{self.variant.quantity} left in stock.") if self.variant.quantity - self.quantity < 0
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Callbacks
|
47
|
+
#
|
48
|
+
|
49
|
+
before_save :update_price
|
50
|
+
after_save { self.order.calculate }
|
51
|
+
|
52
|
+
#
|
53
|
+
# Methods
|
54
|
+
#
|
55
|
+
|
56
|
+
def update_price
|
57
|
+
self.price = self.variant.price * self.quantity
|
58
|
+
end
|
59
|
+
|
60
|
+
def title
|
61
|
+
if self.variant.product.variants.count > 1
|
62
|
+
"#{self.variant.product.title} - #{self.variant.title}"
|
63
|
+
else
|
64
|
+
self.variant.product.title
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def as_json(options={})
|
69
|
+
self.attributes.merge({
|
70
|
+
:variant => self.variant,
|
71
|
+
:title => self.title,
|
72
|
+
:product => { :images => self.variant.product.product_images }
|
73
|
+
})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -7,12 +7,10 @@ module CabooseStore
|
|
7
7
|
attr_accessor :name, :email, :body
|
8
8
|
|
9
9
|
validates :name, :email, :body, presence: true
|
10
|
-
validates :email, format: { with
|
10
|
+
validates :email, format: { :with => %r{.+@.+\..+} }, allow_blank: false
|
11
11
|
|
12
|
-
def initialize(attributes
|
13
|
-
attributes.each
|
14
|
-
send("#{name}=", value)
|
15
|
-
end
|
12
|
+
def initialize(attributes={})
|
13
|
+
attributes.each { |name, value| send("#{name}=", value) }
|
16
14
|
end
|
17
15
|
|
18
16
|
def persisted?
|
@@ -1,97 +1,187 @@
|
|
1
|
+
#
|
2
|
+
# Order
|
3
|
+
#
|
4
|
+
|
1
5
|
module CabooseStore
|
2
6
|
class Order < ActiveRecord::Base
|
3
|
-
self.table_name
|
7
|
+
self.table_name = 'store_orders'
|
8
|
+
self.primary_key = 'id'
|
4
9
|
|
5
10
|
belongs_to :customer, :class_name => 'Caboose::User'
|
6
11
|
belongs_to :shipping_address, :class_name => 'Address'
|
7
12
|
belongs_to :billing_address, :class_name => 'Address'
|
8
13
|
has_many :discounts, :through => :order_discounts
|
9
14
|
has_many :order_discounts
|
10
|
-
has_many :
|
15
|
+
has_many :line_items, :after_add => :line_item_added, :after_remove => :line_item_removed
|
11
16
|
|
12
17
|
attr_accessible :id,
|
13
|
-
:order_number,
|
14
|
-
:subtotal,
|
18
|
+
:order_number,
|
19
|
+
:subtotal,
|
15
20
|
:tax,
|
21
|
+
:shipping_code,
|
16
22
|
:shipping_method,
|
17
|
-
:shipping_method_code,
|
18
23
|
:shipping,
|
24
|
+
:handling,
|
19
25
|
:discount,
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
26
|
+
:amount_discounted,
|
27
|
+
:total,
|
28
|
+
:status,
|
29
|
+
:payment_status,
|
30
|
+
:notes,
|
31
|
+
:referring_site,
|
32
|
+
:landing_site,
|
33
|
+
:landing_site_ref,
|
34
|
+
:cancel_reason,
|
28
35
|
:date_created,
|
29
36
|
:date_authorized,
|
30
37
|
:date_captured,
|
31
|
-
:date_cancelled
|
32
|
-
|
33
|
-
|
34
|
-
|
38
|
+
:date_cancelled,
|
39
|
+
:email,
|
40
|
+
:customer_id,
|
41
|
+
:payment_id,
|
42
|
+
:gateway_id,
|
43
|
+
:financial_status,
|
44
|
+
:shipping_address_id,
|
45
|
+
:billing_address_id,
|
46
|
+
:landing_page,
|
47
|
+
:landing_page_ref,
|
48
|
+
:transaction_d,
|
49
|
+
:auth_code,
|
50
|
+
:alternate_id,
|
51
|
+
:auth_amount,
|
52
|
+
:date_shipped,
|
53
|
+
:transaction_service,
|
54
|
+
:transaction_id
|
55
|
+
|
56
|
+
#
|
57
|
+
# Scopes
|
58
|
+
#
|
59
|
+
|
60
|
+
scope :test, where('status = ?', 'testing')
|
61
|
+
scope :cancelled, where('status = ?', 'cancelled')
|
62
|
+
scope :pending, where('status = ?', 'pending')
|
63
|
+
#TODO scope :fulfilled
|
64
|
+
#TODO scope :unfulfilled
|
65
|
+
scope :authorized, where('financial_status = ?', 'authorized')
|
66
|
+
scope :captured, where('financial_status = ?', 'captured')
|
67
|
+
scope :refunded, where('financial_status = ?', 'refunded')
|
68
|
+
scope :voided, where('financial_status = ?', 'voided')
|
69
|
+
|
70
|
+
#
|
71
|
+
# Validations
|
72
|
+
#
|
73
|
+
|
74
|
+
validates :status, :inclusion => {
|
75
|
+
:in => ['cart', 'pending', 'cancelled', 'shipped', 'testing'],
|
76
|
+
:message => "%{value} is not a valid status. Must be either 'pending' or 'shipped'"
|
77
|
+
}
|
78
|
+
|
79
|
+
validates :financial_status, :inclusion => {
|
80
|
+
:in => ['pending', 'authorized', 'captured', 'refunded', 'voided'],
|
81
|
+
:message => "%{value} is not a valid financial status. Must be 'authorized', 'captured', 'refunded' or 'voided'"
|
82
|
+
}
|
83
|
+
|
84
|
+
#
|
85
|
+
# Callbacks
|
86
|
+
#
|
87
|
+
|
88
|
+
after_update :calculate
|
89
|
+
|
90
|
+
#
|
91
|
+
# Methods
|
92
|
+
#
|
93
|
+
|
94
|
+
def as_json(options={})
|
95
|
+
self.attributes.merge({
|
96
|
+
:line_items => self.line_items,
|
97
|
+
:shipping_address => self.shipping_address,
|
98
|
+
:billing_address => self.billing_address
|
99
|
+
})
|
35
100
|
end
|
36
101
|
|
37
|
-
def
|
38
|
-
return
|
102
|
+
def decrement_quantities
|
103
|
+
return false if self.decremented
|
104
|
+
|
105
|
+
self.line_items.each do |line_item|
|
106
|
+
line_item.variant.update_attribute(:quantit, line_item.variant.quantity - line_item.quantity)
|
107
|
+
end
|
108
|
+
|
109
|
+
self.update_attribute(:decremented, true)
|
39
110
|
end
|
40
111
|
|
41
|
-
|
42
|
-
|
43
|
-
|
112
|
+
def increment_quantities
|
113
|
+
return false if !self.decremented
|
114
|
+
|
115
|
+
self.line_items.each do |line_item|
|
116
|
+
line_item.variant.update_attribute(:quantity, line_item.variant.quantity - line_item.quantity)
|
117
|
+
end
|
118
|
+
|
119
|
+
self.update_attribute(:decremented, false)
|
44
120
|
end
|
45
121
|
|
46
|
-
|
47
|
-
|
48
|
-
return self.order_line_items.where('status != ?', 'shipped').all
|
122
|
+
def resend_confirmation
|
123
|
+
OrdersMailer.customer_new_order(self).deliver
|
49
124
|
end
|
50
125
|
|
51
|
-
def
|
52
|
-
|
126
|
+
def test?
|
127
|
+
self.status == 'testing'
|
53
128
|
end
|
54
129
|
|
55
|
-
|
56
|
-
|
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
|
130
|
+
def authorized?
|
131
|
+
self.financial_status == 'authorized'
|
65
132
|
end
|
66
133
|
|
67
|
-
def
|
68
|
-
self
|
69
|
-
self.total = self.subtotal + self.tax + self.shipping + self.handling - self.discount
|
70
|
-
return self.total
|
134
|
+
def capture
|
135
|
+
PaymentProcessor.capture(self)
|
71
136
|
end
|
72
137
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
self.
|
94
|
-
|
138
|
+
def refuned
|
139
|
+
PaymentProcessor.refund(self)
|
140
|
+
end
|
141
|
+
|
142
|
+
def void
|
143
|
+
PaymentProcessor.void(self)
|
144
|
+
end
|
145
|
+
|
146
|
+
def line_item_added(line_item)
|
147
|
+
self.calculate
|
148
|
+
end
|
149
|
+
|
150
|
+
def line_item_removed(line_item)
|
151
|
+
self.calculate
|
152
|
+
end
|
153
|
+
|
154
|
+
def calculate
|
155
|
+
self.update_column(:subtotal, (self.calculate_subtotal * 100).ceil / 100.00)
|
156
|
+
self.update_column(:tax, (self.calculate_tax * 100).ceil / 100.00)
|
157
|
+
self.update_column(:shipping, (self.calculate_shipping * 100).ceil / 100.00)
|
158
|
+
self.update_column(:handling, (self.calculate_handling * 100).ceil / 100.00)
|
159
|
+
self.update_column(:total, (self.calculate_total * 100).ceil / 100.00)
|
160
|
+
end
|
161
|
+
|
162
|
+
def calculate_subtotal
|
163
|
+
return 0 if self.line_items.empty?
|
164
|
+
self.line_items.collect { |line_item| line_item.price }.inject { |sum, price| sum + price }
|
165
|
+
end
|
166
|
+
|
167
|
+
def calculate_tax
|
168
|
+
return 0 if !self.shipping_address
|
169
|
+
TaxCalculator.tax_rate(self.shipping_address)
|
170
|
+
end
|
171
|
+
|
172
|
+
def calculate_shipping
|
173
|
+
return 0 if !self.shipping_address || !self.shipping_code
|
174
|
+
ShippingCalculator.rate(self)[:total_price]
|
175
|
+
end
|
176
|
+
|
177
|
+
def calculate_handling
|
178
|
+
return 0 if !CabooseStore::handling_percentage
|
179
|
+
self.shipping * CabooseStore::handling_percentage
|
180
|
+
end
|
181
|
+
|
182
|
+
def calculate_total
|
183
|
+
[self.subtotal, self.tax, self.shipping, self.handling].compact.inject { |sum, price| sum + price }
|
95
184
|
end
|
96
185
|
end
|
97
186
|
end
|
187
|
+
|
@@ -1,11 +1,10 @@
|
|
1
1
|
module CabooseStore
|
2
2
|
class OrderDiscount < ActiveRecord::Base
|
3
|
-
self.table_name =
|
3
|
+
self.table_name = 'store_order_discounts'
|
4
4
|
|
5
5
|
belongs_to :order
|
6
6
|
belongs_to :discount
|
7
7
|
|
8
8
|
attr_accessible :order_id, :discount_id
|
9
|
-
|
10
9
|
end
|
11
10
|
end
|
@@ -4,7 +4,7 @@ module CabooseStore
|
|
4
4
|
class OrderPdf < Prawn::Document
|
5
5
|
|
6
6
|
attr_accessor :order
|
7
|
-
|
7
|
+
|
8
8
|
def to_pdf
|
9
9
|
image open("https://dmwwflw4i3miv.cloudfront.net/logo.png"), :position => :center
|
10
10
|
text " "
|
@@ -73,10 +73,8 @@ module CabooseStore
|
|
73
73
|
tbl << [{ :content => "#{order.shipping_method} Shipping & Handling" , :colspan => 5, :align => :right }, { :content => sprintf("%.2f", order.shipping + order.handling) , :align => :right }]
|
74
74
|
tbl << [{ :content => "Discount" , :colspan => 5, :align => :right }, { :content => sprintf("%.2f", order.discount ) , :align => :right }]
|
75
75
|
tbl << [{ :content => "Total" , :colspan => 5, :align => :right }, { :content => sprintf("%.2f", order.total ) , :align => :right }]
|
76
|
-
|
77
|
-
table tbl
|
78
76
|
|
77
|
+
table tbl
|
79
78
|
end
|
80
|
-
|
81
79
|
end
|
82
80
|
end
|
@@ -1,3 +1,21 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
class CabooseStore::PaymentProcessors::Authorizenet < CabooseStore::PaymentProcessors::Base
|
2
|
+
def self.api(root, body, test=false)
|
3
|
+
end
|
4
|
+
|
5
|
+
def self.form_url(order)
|
6
|
+
#'https://secure.authorize.net/gateway/transact.dll'
|
7
|
+
'https://test.authorize.net/gateway/transact.dll'
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.authorize(order, params)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.void(order)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.capture(order)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.refund(order)
|
20
|
+
end
|
3
21
|
end
|
@@ -1,3 +1,93 @@
|
|
1
|
+
require "rexml/document"
|
1
2
|
|
2
|
-
class CabooseStore::PaymentProcessors::Payscape
|
3
|
+
class CabooseStore::PaymentProcessors::Payscape < CabooseStore::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
|
+
CabooseStore::api_key
|
11
|
+
end
|
12
|
+
|
13
|
+
ap "API key used: #{body['api-key']}"
|
14
|
+
# ap "AUTH USERNAME: #{CabooseStore::payscape_username}"
|
15
|
+
# ap "AUTH PASSWORD: #{CabooseStore::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' => "#{CabooseStore::root_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=#{CabooseStore::payscape_username}&password=#{CabooseStore::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
|
+
return response['result-code'].to_i == 100
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.void(order)
|
76
|
+
response = self.api 'void', { 'transaction-id' => order.transaction_id }, order.test?
|
77
|
+
return response['result-code'].to_i == 100
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.capture(order)
|
81
|
+
response = self.api 'capture', { 'transaction-id' => order.transaction_id }, order.test?
|
82
|
+
return response['result-code'].to_i == 100
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.refund(order)
|
86
|
+
response = self.api 'refund', {
|
87
|
+
'transaction-id' => order.transaction_id,
|
88
|
+
'amount' => order.total.to_s
|
89
|
+
}, order.test?
|
90
|
+
|
91
|
+
return response['result-code'].to_i == 100
|
92
|
+
end
|
3
93
|
end
|
@@ -1,21 +1,29 @@
|
|
1
|
-
module CabooseStore
|
1
|
+
module CabooseStore
|
2
2
|
class Product < ActiveRecord::Base
|
3
|
-
self.table_name =
|
3
|
+
self.table_name = 'store_products'
|
4
|
+
self.primary_key = 'id'
|
4
5
|
|
5
6
|
belongs_to :vendor, :class_name => 'CabooseStore::Vendor'
|
6
|
-
has_many :
|
7
|
-
has_many :
|
8
|
-
has_many :
|
9
|
-
has_many :
|
10
|
-
has_many :
|
11
|
-
|
7
|
+
has_many :customizations, :class_name => 'CabooseStore::Product', :through => :customization_memberships
|
8
|
+
has_many :customization_memberships, :class_name => 'CabooseStore::CustomizationMembership'
|
9
|
+
has_many :categories, :class_name => 'CabooseStore::Category', :through => :category_memberships
|
10
|
+
has_many :category_memberships, :class_name => 'CabooseStore::CategoryMembership'
|
11
|
+
has_many :variants, :class_name => 'CabooseStore::Variant'
|
12
|
+
has_many :product_images, :class_name => 'CabooseStore::ProductImage'
|
13
|
+
has_many :proudct_inputs, :class_name => 'CabooseStore::ProductInput'
|
14
|
+
has_many :reviews, :class_name => 'CabooseStore::Review'
|
15
|
+
|
16
|
+
default_scope order('store_products.sort_order')
|
17
|
+
|
12
18
|
attr_accessible :id,
|
19
|
+
:alternate_id,
|
13
20
|
:title,
|
14
21
|
:description,
|
15
|
-
:vendor_id,
|
22
|
+
:vendor_id,
|
16
23
|
:handle,
|
24
|
+
:date_available,
|
17
25
|
:seo_title,
|
18
|
-
:seo_description,
|
26
|
+
:seo_description,
|
19
27
|
:option1,
|
20
28
|
:option2,
|
21
29
|
:option3,
|
@@ -23,20 +31,58 @@ module CabooseStore
|
|
23
31
|
:default2,
|
24
32
|
:default3,
|
25
33
|
:status,
|
26
|
-
:
|
34
|
+
:custom_input,
|
35
|
+
:featured,
|
36
|
+
:sort_order
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# Scopes
|
41
|
+
#
|
42
|
+
|
43
|
+
scope :featured, -> { where(:featured => true) }
|
44
|
+
|
45
|
+
#
|
46
|
+
# Class Methods
|
47
|
+
#
|
48
|
+
|
49
|
+
def self.with_images
|
50
|
+
joins(:product_images)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.active
|
54
|
+
where(:status => 'Active')
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.by_title
|
58
|
+
order('title')
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Instance Methods
|
63
|
+
#
|
64
|
+
|
65
|
+
def as_json(options={})
|
66
|
+
self.attributes.merge({
|
67
|
+
:variants => self.variants,
|
68
|
+
:images => self.product_images
|
69
|
+
})
|
70
|
+
end
|
27
71
|
|
28
72
|
def options
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
73
|
+
options = []
|
74
|
+
|
75
|
+
options << self.option1 if !self.option1.nil? && self.option1.strip.length > 0
|
76
|
+
options << self.option2 if !self.option2.nil? && self.option2.strip.length > 0
|
77
|
+
options << self.option3 if !self.option3.nil? && self.option3.strip.length > 0
|
78
|
+
|
79
|
+
return options
|
34
80
|
end
|
35
|
-
|
81
|
+
|
36
82
|
def most_popular_variant
|
37
|
-
|
83
|
+
self.variants.where('price > ? AND status != ?', 0, 'Deleted').order('price ASC').first
|
38
84
|
end
|
39
|
-
|
85
|
+
|
40
86
|
def featured_image
|
41
87
|
self.product_images.reject{|p| p.nil?}.first
|
42
88
|
end
|
@@ -49,30 +95,40 @@ module CabooseStore
|
|
49
95
|
def price_range
|
50
96
|
min = 100000
|
51
97
|
max = 0
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
98
|
+
|
99
|
+
variants.each do |variant|
|
100
|
+
next if variant.nil? or variant.price.nil? or variant.price < 0
|
101
|
+
min = variant.price if variant.price < min
|
102
|
+
max = variant.price if variant.price > max
|
56
103
|
end
|
104
|
+
|
57
105
|
return [min, max]
|
58
106
|
end
|
59
107
|
|
60
108
|
def url
|
61
|
-
|
109
|
+
"/products/#{self.id}"
|
62
110
|
end
|
63
111
|
|
64
112
|
def related_items
|
65
|
-
|
113
|
+
Array.new # TODO should be obvious
|
66
114
|
end
|
67
115
|
|
68
116
|
def in_stock
|
69
|
-
|
117
|
+
Variant.where(:product_id => self.id).where('quantity > 0').count > 0
|
70
118
|
end
|
71
119
|
|
72
|
-
|
73
|
-
|
74
|
-
|
120
|
+
def input_required?
|
121
|
+
!self.custom_input.nil?
|
122
|
+
end
|
123
|
+
|
124
|
+
def active_customizations
|
125
|
+
self.customizations.where(:status => 'Active')
|
75
126
|
end
|
127
|
+
|
128
|
+
def live_variants
|
76
129
|
|
130
|
+
# Return variants that haven't been "deleted"
|
131
|
+
self.variants.where(:status => ['Active', 'Inactive'])
|
132
|
+
end
|
77
133
|
end
|
78
|
-
end
|
134
|
+
end
|