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.
Files changed (143) hide show
  1. checksums.yaml +8 -8
  2. data/app/assets/javascripts/caboose/main.js +8 -0
  3. data/app/assets/javascripts/caboose_store/admin.js +0 -19
  4. data/app/assets/javascripts/caboose_store/admin_products.js +3 -4
  5. data/app/assets/javascripts/caboose_store/application.js +6 -5
  6. data/app/assets/javascripts/caboose_store/cart.js.bak +258 -0
  7. data/app/assets/javascripts/caboose_store/checkout.js +151 -0
  8. data/app/assets/javascripts/caboose_store/config.js +83 -0
  9. data/app/assets/javascripts/caboose_store/jquery-ui-multisortable.js +254 -0
  10. data/app/assets/javascripts/caboose_store/jquery-ui.js +3572 -0
  11. data/app/assets/javascripts/caboose_store/main.js +20 -0
  12. data/app/assets/javascripts/caboose_store/modules/cart.js +167 -0
  13. data/app/assets/javascripts/caboose_store/modules/checkout.js +313 -0
  14. data/app/assets/javascripts/caboose_store/modules/product.js +282 -0
  15. data/app/assets/stylesheets/caboose_store/admin.css +3 -3
  16. data/app/assets/stylesheets/caboose_store/application.css +2 -2
  17. data/app/assets/stylesheets/caboose_store/modal.css +1 -0
  18. data/app/assets/templates/caboose_store/cart/add_to_cart.jst.ejs +7 -0
  19. data/app/assets/templates/caboose_store/cart/line_items.jst.ejs +41 -0
  20. data/app/assets/templates/caboose_store/checkout/address.jst.ejs +53 -0
  21. data/app/assets/templates/caboose_store/checkout/forms/guest.jst.ejs +8 -0
  22. data/app/assets/templates/caboose_store/checkout/forms/register.jst.ejs +11 -0
  23. data/app/assets/templates/caboose_store/checkout/forms/signin.jst.ejs +7 -0
  24. data/app/assets/templates/caboose_store/checkout/line_items.jst.ejs +32 -0
  25. data/app/assets/templates/caboose_store/checkout/login.jst.ejs +21 -0
  26. data/app/assets/templates/caboose_store/checkout/payment.jst.ejs +5 -0
  27. data/app/assets/templates/caboose_store/checkout/shipping.jst.ejs +18 -0
  28. data/app/assets/templates/caboose_store/product/images.jst.ejs +8 -0
  29. data/app/assets/templates/caboose_store/product/options.jst.ejs +19 -0
  30. data/app/controllers/caboose_store/application_controller.rb +1 -11
  31. data/app/controllers/caboose_store/cart_controller.rb +36 -44
  32. data/app/controllers/caboose_store/cart_controller.rb.bak +169 -0
  33. data/app/controllers/caboose_store/categories_controller.rb +72 -92
  34. data/app/controllers/caboose_store/checkout_controller.rb +231 -151
  35. data/app/controllers/caboose_store/orders_controller.rb +235 -95
  36. data/app/controllers/caboose_store/product_images_controller.rb +1 -1
  37. data/app/controllers/caboose_store/products_controller.rb +275 -84
  38. data/app/controllers/caboose_store/reviews_controller.rb +1 -1
  39. data/app/controllers/caboose_store/variants_controller.rb +86 -20
  40. data/app/controllers/caboose_store/vendors_controller.rb +73 -0
  41. data/app/helpers/caboose_store/application_helper.rb +3 -41
  42. data/app/helpers/caboose_store/cart_helper.rb +0 -24
  43. data/app/helpers/caboose_store/categories_helper.rb +69 -25
  44. data/app/helpers/caboose_store/checkout_helper.rb +31 -0
  45. data/app/helpers/caboose_store/products_helper.rb +4 -80
  46. data/app/mailers/caboose_store/orders_mailer.rb +6 -13
  47. data/app/models/caboose_store/address.rb +5 -9
  48. data/app/models/caboose_store/caboose_store_plugin.rb +18 -7
  49. data/app/models/caboose_store/category.rb +66 -41
  50. data/app/models/caboose_store/category_membership.rb +1 -2
  51. data/app/models/caboose_store/customization_membership.rb +10 -0
  52. data/app/models/caboose_store/discount.rb +4 -2
  53. data/app/models/caboose_store/line_item.rb +77 -0
  54. data/app/models/caboose_store/message.rb +3 -5
  55. data/app/models/caboose_store/order.rb +153 -63
  56. data/app/models/caboose_store/order_discount.rb +1 -2
  57. data/app/models/caboose_store/order_pdf.rb +2 -4
  58. data/app/models/caboose_store/payment_processors/authorizenet.rb +20 -2
  59. data/app/models/caboose_store/payment_processors/base.rb +0 -2
  60. data/app/models/caboose_store/payment_processors/payscape.rb +91 -1
  61. data/app/models/caboose_store/product.rb +86 -30
  62. data/app/models/caboose_store/product_image.rb +50 -15
  63. data/app/models/caboose_store/product_image_variant.rb +3 -3
  64. data/app/models/caboose_store/review.rb +8 -8
  65. data/app/models/caboose_store/schema.rb +174 -119
  66. data/app/models/caboose_store/search_filter.rb +110 -115
  67. data/app/models/caboose_store/shipping_calculator.rb +51 -49
  68. data/app/models/caboose_store/tax_calculator.rb +2 -6
  69. data/app/models/caboose_store/tax_line.rb +1 -4
  70. data/app/models/caboose_store/variant.rb +77 -21
  71. data/app/models/caboose_store/vendor.rb +17 -2
  72. data/app/views/caboose_store/cart/index.html.erb +3 -18
  73. data/app/views/caboose_store/categories/admin_edit.html.erb +64 -67
  74. data/app/views/caboose_store/categories/admin_index.html.erb +7 -9
  75. data/app/views/caboose_store/categories/admin_new.html.erb +56 -39
  76. data/app/views/caboose_store/checkout/_address_form.html.erb +111 -0
  77. data/app/views/caboose_store/checkout/_billing_form.html.erb +47 -0
  78. data/app/views/caboose_store/checkout/_cart.html.erb +52 -0
  79. data/app/views/caboose_store/checkout/_order_discount.html.erb +40 -0
  80. data/app/views/caboose_store/checkout/_shipping_address.html.erb +10 -0
  81. data/app/views/caboose_store/checkout/_shipping_method.html.erb +2 -0
  82. data/app/views/caboose_store/checkout/_shipping_method_form.html.erb +21 -0
  83. data/app/views/caboose_store/checkout/billing.html.erb +4 -161
  84. data/app/views/caboose_store/checkout/discount.html.erb +5 -160
  85. data/app/views/caboose_store/checkout/empty.html.erb +2 -0
  86. data/app/views/caboose_store/checkout/error.html.erb +2 -0
  87. data/app/views/caboose_store/checkout/index.html.erb +30 -100
  88. data/app/views/caboose_store/checkout/login.html.erb +2 -0
  89. data/app/views/caboose_store/checkout/payment.html.erb +79 -0
  90. data/app/views/caboose_store/checkout/relay.html.erb +12 -0
  91. data/app/views/caboose_store/checkout/relay_postMessage.html.erb +19 -0
  92. data/app/views/caboose_store/checkout/shipping.html.erb +8 -83
  93. data/app/views/caboose_store/checkout/step_one.html.erb +15 -0
  94. data/app/views/caboose_store/checkout/step_two.html.erb +15 -0
  95. data/app/views/caboose_store/checkout/thanks.html.erb +5 -0
  96. data/app/views/caboose_store/layouts/_footer.html.erb +1 -1
  97. data/app/views/caboose_store/orders/admin_edit.html.erb +101 -7
  98. data/app/views/caboose_store/orders/admin_index.html.erb +8 -8
  99. data/app/views/caboose_store/orders_mailer/customer_new_order.html.erb +0 -46
  100. data/app/views/caboose_store/orders_mailer/customer_status_updated.html.erb +0 -48
  101. data/app/views/caboose_store/orders_mailer/fulfillment_new_order.html.erb +0 -42
  102. data/app/views/caboose_store/orders_mailer/shipping_order_ready.html.erb +0 -45
  103. data/app/views/caboose_store/products/_admin_header.html.erb +7 -6
  104. data/app/views/caboose_store/products/_sort_options.html.erb +19 -0
  105. data/app/views/caboose_store/products/admin_add_upcs.html.erb +58 -0
  106. data/app/views/caboose_store/products/admin_edit_categories.html.erb +1 -1
  107. data/app/views/caboose_store/products/admin_edit_description.html.erb +2 -5
  108. data/app/views/caboose_store/products/admin_edit_general.html.erb +58 -1
  109. data/app/views/caboose_store/products/admin_edit_images.html.erb +3 -1
  110. data/app/views/caboose_store/products/admin_edit_variants.html.erb +13 -2
  111. data/app/views/caboose_store/products/admin_edit_variants_single.html.erb +14 -14
  112. data/app/views/caboose_store/products/admin_group_variants.html.erb +433 -0
  113. data/app/views/caboose_store/products/admin_index.html.erb +87 -39
  114. data/app/views/caboose_store/products/admin_sort.html copy.erb +155 -0
  115. data/app/views/caboose_store/products/admin_sort.html.erb +254 -0
  116. data/app/views/caboose_store/products/details.html.erb +1 -2
  117. data/app/views/caboose_store/variants/admin_edit.html.erb +18 -16
  118. data/app/views/caboose_store/variants/admin_group.html.erb +184 -0
  119. data/app/views/caboose_store/vendors/admin_edit.html.erb +24 -0
  120. data/app/views/caboose_store/vendors/admin_index.html.erb +30 -0
  121. data/app/views/caboose_store/vendors/admin_new.html.erb +34 -0
  122. data/config/routes.rb +65 -27
  123. data/lib/caboose-store/caboose_store_helper.rb +29 -30
  124. data/lib/caboose-store/engine.rb +73 -28
  125. data/lib/caboose-store/version.rb +1 -1
  126. data/lib/caboose-store.rb +16 -7
  127. data/lib/tasks/caboose-store.rake +0 -2
  128. metadata +54 -17
  129. data/app/assets/javascripts/caboose_store/modal.js +0 -52
  130. data/app/assets/javascripts/caboose_store/modal_integration.js +0 -25
  131. data/app/assets/javascripts/caboose_store/model.form.page.js +0 -30
  132. data/app/assets/javascripts/caboose_store/model.form.user.js +0 -36
  133. data/app/assets/javascripts/caboose_store/payment_processors/authorizenet.js +0 -21
  134. data/app/assets/javascripts/caboose_store/payment_processors/base.js +0 -21
  135. data/app/assets/javascripts/caboose_store/payment_processors/payscape.js +0 -21
  136. data/app/assets/javascripts/caboose_store/shortcut.js +0 -11
  137. data/app/assets/javascripts/caboose_store/station.js +0 -60
  138. data/app/models/caboose_store/order_line_item.rb +0 -13
  139. data/app/models/caboose_store/states.rb +0 -60
  140. data/app/views/caboose_store/application/_category_thumb.html.erb +0 -6
  141. data/app/views/caboose_store/application/_product_thumb.html.erb +0 -13
  142. data/app/views/caboose_store/checkout/quantity_box.html.erb +0 -39
  143. 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: %r{.+@.+\..+} }, allow_blank: false
10
+ validates :email, format: { :with => %r{.+@.+\..+} }, allow_blank: false
11
11
 
12
- def initialize(attributes = {})
13
- attributes.each do |name, value|
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 = "store_orders"
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 :order_line_items
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, # Customer-set unique number of the order
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
- :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'
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
- def line_items
34
- return self.order_line_items
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 subtotal
38
- return self.order_line_items.inject(0.0){ |sum,li| sum + li.subtotal }
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
- # 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
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
- # 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
122
+ def resend_confirmation
123
+ OrdersMailer.customer_new_order(self).deliver
49
124
  end
50
125
 
51
- def cancelled
52
- return self.status == 'cancelled'
126
+ def test?
127
+ self.status == 'testing'
53
128
  end
54
129
 
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
130
+ def authorized?
131
+ self.financial_status == 'authorized'
65
132
  end
66
133
 
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
134
+ def capture
135
+ PaymentProcessor.capture(self)
71
136
  end
72
137
 
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
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 = "store_order_discounts"
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
- class CabooseStore::PaymentProcessors::Authorizenet > CabooseStore::PaymentProcessors::Base
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,4 +1,3 @@
1
-
2
1
  class CabooseStore::PaymentProcessors::Base
3
2
 
4
3
  # Gets a transaction object that obfuscates transaction info.
@@ -38,4 +37,3 @@ class CabooseStore::PaymentProcessors::Base
38
37
  return false
39
38
  end
40
39
  end
41
-
@@ -1,3 +1,93 @@
1
+ require "rexml/document"
1
2
 
2
- class CabooseStore::PaymentProcessors::Payscape > CabooseStore::PaymentProcessors::Base
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 = "store_products"
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 :categories, :through => :category_memberships, :class_name => 'CabooseStore::Category'
7
- has_many :category_memberships , :class_name => 'CabooseStore::CategoryMembership'
8
- has_many :variants , :class_name => 'CabooseStore::Variant'
9
- has_many :product_images , :class_name => 'CabooseStore::ProductImage'
10
- has_many :reviews , :class_name => 'CabooseStore::Review'
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
- :alternate_id
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
- 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
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
- self.variants.order('price DESC').first
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
- 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
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
- return "/products/#{self.id}"
109
+ "/products/#{self.id}"
62
110
  end
63
111
 
64
112
  def related_items
65
- return []
113
+ Array.new # TODO should be obvious
66
114
  end
67
115
 
68
116
  def in_stock
69
- return Variant.where(:product_id => self.id).where("quantity_in_stock > 0").count > 0
117
+ Variant.where(:product_id => self.id).where('quantity > 0').count > 0
70
118
  end
71
119
 
72
- # Returns non deleted variants
73
- def live_variants
74
- return self.variants.where(:status => ['Active', 'Inactive'])
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