effective_orders 1.0.0

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.
Files changed (146) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +856 -0
  4. data/Rakefile +24 -0
  5. data/app/assets/images/effective_orders/stripe_connect.png +0 -0
  6. data/app/assets/javascripts/effective_orders/shipping_address_toggle.js.coffee +30 -0
  7. data/app/assets/javascripts/effective_orders/stripe_charges.js.coffee +26 -0
  8. data/app/assets/javascripts/effective_orders/stripe_subscriptions.js.coffee +28 -0
  9. data/app/assets/javascripts/effective_orders.js +2 -0
  10. data/app/assets/stylesheets/effective_orders/_order.scss +30 -0
  11. data/app/assets/stylesheets/effective_orders.css.scss +1 -0
  12. data/app/controllers/admin/customers_controller.rb +15 -0
  13. data/app/controllers/admin/orders_controller.rb +22 -0
  14. data/app/controllers/effective/carts_controller.rb +70 -0
  15. data/app/controllers/effective/orders_controller.rb +191 -0
  16. data/app/controllers/effective/providers/moneris.rb +94 -0
  17. data/app/controllers/effective/providers/paypal.rb +29 -0
  18. data/app/controllers/effective/providers/stripe.rb +125 -0
  19. data/app/controllers/effective/providers/stripe_connect.rb +47 -0
  20. data/app/controllers/effective/subscriptions_controller.rb +123 -0
  21. data/app/controllers/effective/webhooks_controller.rb +86 -0
  22. data/app/helpers/effective_carts_helper.rb +90 -0
  23. data/app/helpers/effective_orders_helper.rb +108 -0
  24. data/app/helpers/effective_paypal_helper.rb +37 -0
  25. data/app/helpers/effective_stripe_helper.rb +63 -0
  26. data/app/mailers/effective/orders_mailer.rb +64 -0
  27. data/app/models/concerns/acts_as_purchasable.rb +134 -0
  28. data/app/models/effective/access_denied.rb +17 -0
  29. data/app/models/effective/cart.rb +65 -0
  30. data/app/models/effective/cart_item.rb +40 -0
  31. data/app/models/effective/customer.rb +61 -0
  32. data/app/models/effective/datatables/customers.rb +45 -0
  33. data/app/models/effective/datatables/orders.rb +53 -0
  34. data/app/models/effective/order.rb +247 -0
  35. data/app/models/effective/order_item.rb +69 -0
  36. data/app/models/effective/stripe_charge.rb +35 -0
  37. data/app/models/effective/subscription.rb +95 -0
  38. data/app/models/inputs/price_field.rb +63 -0
  39. data/app/models/inputs/price_form_input.rb +7 -0
  40. data/app/models/inputs/price_formtastic_input.rb +9 -0
  41. data/app/models/inputs/price_input.rb +19 -0
  42. data/app/models/inputs/price_simple_form_input.rb +8 -0
  43. data/app/models/validators/effective/sold_out_validator.rb +7 -0
  44. data/app/views/active_admin/effective_orders/orders/_show.html.haml +70 -0
  45. data/app/views/admin/customers/_actions.html.haml +2 -0
  46. data/app/views/admin/customers/index.html.haml +10 -0
  47. data/app/views/admin/orders/index.html.haml +7 -0
  48. data/app/views/admin/orders/show.html.haml +11 -0
  49. data/app/views/effective/carts/_cart.html.haml +33 -0
  50. data/app/views/effective/carts/show.html.haml +18 -0
  51. data/app/views/effective/orders/_checkout_step_1.html.haml +39 -0
  52. data/app/views/effective/orders/_checkout_step_2.html.haml +18 -0
  53. data/app/views/effective/orders/_my_purchases.html.haml +15 -0
  54. data/app/views/effective/orders/_order.html.haml +4 -0
  55. data/app/views/effective/orders/_order_header.html.haml +21 -0
  56. data/app/views/effective/orders/_order_items.html.haml +39 -0
  57. data/app/views/effective/orders/_order_payment_details.html.haml +11 -0
  58. data/app/views/effective/orders/_order_shipping.html.haml +19 -0
  59. data/app/views/effective/orders/_order_user_fields.html.haml +10 -0
  60. data/app/views/effective/orders/checkout.html.haml +3 -0
  61. data/app/views/effective/orders/declined.html.haml +10 -0
  62. data/app/views/effective/orders/moneris/_form.html.haml +34 -0
  63. data/app/views/effective/orders/my_purchases.html.haml +6 -0
  64. data/app/views/effective/orders/my_sales.html.haml +28 -0
  65. data/app/views/effective/orders/new.html.haml +4 -0
  66. data/app/views/effective/orders/paypal/_form.html.haml +5 -0
  67. data/app/views/effective/orders/purchased.html.haml +10 -0
  68. data/app/views/effective/orders/show.html.haml +17 -0
  69. data/app/views/effective/orders/stripe/_form.html.haml +8 -0
  70. data/app/views/effective/orders/stripe/_subscription_fields.html.haml +7 -0
  71. data/app/views/effective/orders_mailer/order_receipt_to_admin.html.haml +8 -0
  72. data/app/views/effective/orders_mailer/order_receipt_to_buyer.html.haml +8 -0
  73. data/app/views/effective/orders_mailer/order_receipt_to_seller.html.haml +30 -0
  74. data/app/views/effective/subscriptions/index.html.haml +16 -0
  75. data/app/views/effective/subscriptions/new.html.haml +10 -0
  76. data/app/views/effective/subscriptions/show.html.haml +49 -0
  77. data/config/routes.rb +57 -0
  78. data/db/migrate/01_create_effective_orders.rb.erb +91 -0
  79. data/db/upgrade/02_upgrade_effective_orders_from03x.rb.erb +29 -0
  80. data/db/upgrade/upgrade_price_column_on_table.rb.erb +17 -0
  81. data/lib/effective_orders/engine.rb +52 -0
  82. data/lib/effective_orders/version.rb +3 -0
  83. data/lib/effective_orders.rb +76 -0
  84. data/lib/generators/effective_orders/install_generator.rb +38 -0
  85. data/lib/generators/effective_orders/upgrade_from03x_generator.rb +34 -0
  86. data/lib/generators/effective_orders/upgrade_price_column_generator.rb +34 -0
  87. data/lib/generators/templates/README +1 -0
  88. data/lib/generators/templates/effective_orders.rb +210 -0
  89. data/spec/controllers/carts_controller_spec.rb +143 -0
  90. data/spec/controllers/moneris_orders_controller_spec.rb +245 -0
  91. data/spec/controllers/orders_controller_spec.rb +418 -0
  92. data/spec/controllers/stripe_orders_controller_spec.rb +127 -0
  93. data/spec/controllers/webhooks_controller_spec.rb +79 -0
  94. data/spec/dummy/README.rdoc +8 -0
  95. data/spec/dummy/Rakefile +6 -0
  96. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  97. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  98. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  99. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  100. data/spec/dummy/app/models/product.rb +17 -0
  101. data/spec/dummy/app/models/product_with_float_price.rb +17 -0
  102. data/spec/dummy/app/models/user.rb +28 -0
  103. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  104. data/spec/dummy/bin/bundle +3 -0
  105. data/spec/dummy/bin/rails +4 -0
  106. data/spec/dummy/bin/rake +4 -0
  107. data/spec/dummy/config/application.rb +31 -0
  108. data/spec/dummy/config/boot.rb +5 -0
  109. data/spec/dummy/config/database.yml +25 -0
  110. data/spec/dummy/config/environment.rb +5 -0
  111. data/spec/dummy/config/environments/development.rb +37 -0
  112. data/spec/dummy/config/environments/production.rb +83 -0
  113. data/spec/dummy/config/environments/test.rb +39 -0
  114. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  115. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  116. data/spec/dummy/config/initializers/devise.rb +254 -0
  117. data/spec/dummy/config/initializers/effective_addresses.rb +15 -0
  118. data/spec/dummy/config/initializers/effective_orders.rb +22 -0
  119. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  120. data/spec/dummy/config/initializers/inflections.rb +16 -0
  121. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  122. data/spec/dummy/config/initializers/session_store.rb +3 -0
  123. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  124. data/spec/dummy/config/locales/en.yml +23 -0
  125. data/spec/dummy/config/routes.rb +3 -0
  126. data/spec/dummy/config/secrets.yml +22 -0
  127. data/spec/dummy/config.ru +4 -0
  128. data/spec/dummy/db/schema.rb +142 -0
  129. data/spec/dummy/db/test.sqlite3 +0 -0
  130. data/spec/dummy/log/development.log +487 -0
  131. data/spec/dummy/log/test.log +347 -0
  132. data/spec/dummy/public/404.html +67 -0
  133. data/spec/dummy/public/422.html +67 -0
  134. data/spec/dummy/public/500.html +66 -0
  135. data/spec/dummy/public/favicon.ico +0 -0
  136. data/spec/helpers/effective_orders_helper_spec.rb +21 -0
  137. data/spec/models/acts_as_purchasable_spec.rb +107 -0
  138. data/spec/models/customer_spec.rb +71 -0
  139. data/spec/models/factories_spec.rb +13 -0
  140. data/spec/models/order_item_spec.rb +35 -0
  141. data/spec/models/order_spec.rb +323 -0
  142. data/spec/models/stripe_charge_spec.rb +39 -0
  143. data/spec/models/subscription_spec.rb +103 -0
  144. data/spec/spec_helper.rb +44 -0
  145. data/spec/support/factories.rb +118 -0
  146. metadata +387 -0
@@ -0,0 +1,64 @@
1
+ module Effective
2
+ class OrdersMailer < ActionMailer::Base
3
+ helper EffectiveOrdersHelper
4
+
5
+ default :from => EffectiveOrders.mailer[:default_from]
6
+
7
+ def order_receipt_to_admin(order)
8
+ @order = order
9
+ mail(:to => EffectiveOrders.mailer[:admin_email], :subject => receipt_to_admin_subject(order))
10
+ end
11
+
12
+ def order_receipt_to_buyer(order) # Buyer
13
+ @order = order
14
+ mail(:to => order.user.email, :subject => receipt_to_buyer_subject(order))
15
+ end
16
+
17
+ def order_receipt_to_seller(order, seller, order_items)
18
+ @order = order
19
+ @user = seller.user
20
+ @order_items = order_items
21
+
22
+ mail(:to => @user.email, :subject => receipt_to_seller_subject(order, order_items, seller.user))
23
+ end
24
+
25
+ private
26
+
27
+ def receipt_to_admin_subject(order)
28
+ string_or_callable = EffectiveOrders.mailer[:subject_for_admin_receipt]
29
+
30
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
31
+ string_or_callable = self.instance_exec(order, &string_or_callable)
32
+ end
33
+
34
+ prefix_subject(string_or_callable.presence || "Order ##{order.to_param} Receipt")
35
+ end
36
+
37
+ def receipt_to_buyer_subject(order)
38
+ string_or_callable = EffectiveOrders.mailer[:subject_for_buyer_receipt]
39
+
40
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
41
+ string_or_callable = self.instance_exec(order, &string_or_callable)
42
+ end
43
+
44
+ prefix_subject(string_or_callable.presence || "Order ##{order.to_param} Receipt")
45
+ end
46
+
47
+ def receipt_to_seller_subject(order, order_items, seller)
48
+ string_or_callable = EffectiveOrders.mailer[:subject_for_seller_receipt]
49
+
50
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
51
+ string_or_callable = self.instance_exec(order, order_items, seller, &string_or_callable)
52
+ end
53
+
54
+ prefix_subject(string_or_callable.presence || "#{order_items.count} of your products #{order_items.count > 1 ? 'have' : 'has'} been purchased")
55
+ end
56
+
57
+ def prefix_subject(text)
58
+ prefix = (EffectiveOrders.mailer[:subject_prefix].to_s rescue '')
59
+ prefix.present? ? (prefix.chomp(' ') + ' ' + text) : text
60
+ end
61
+
62
+ end
63
+ end
64
+
@@ -0,0 +1,134 @@
1
+ module ActsAsPurchasable
2
+ extend ActiveSupport::Concern
3
+
4
+ module ActiveRecord
5
+ def acts_as_purchasable(*options)
6
+ @acts_as_purchasable = options || []
7
+ include ::ActsAsPurchasable
8
+ end
9
+ end
10
+
11
+ included do
12
+ has_many :orders, :through => :order_items, :class_name => 'Effective::Order'
13
+ has_many :order_items, :as => :purchasable, :class_name => 'Effective::OrderItem'
14
+ has_many :cart_items, :as => :purchasable, :dependent => :delete_all, :class_name => 'Effective::CartItem'
15
+
16
+ validates_with Effective::SoldOutValidator, :on => :create
17
+
18
+ validates :price, :presence => true, :numericality => true
19
+ validates :tax_exempt, :inclusion => {:in => [true, false]}
20
+
21
+ # These are breaking on the check for quanitty_enabled?. More research is due
22
+ validates :quantity_purchased, :numericality => {:allow_nil => true}, :if => proc { |purchasable| (purchasable.quantity_enabled? rescue false) }
23
+ validates :quantity_max, :numericality => {:allow_nil => true}, :if => proc { |purchasable| (purchasable.quantity_enabled? rescue false) }
24
+
25
+ scope :purchased, -> { joins(:order_items).joins(:orders).where(:orders => {:purchase_state => EffectiveOrders::PURCHASED}).uniq }
26
+ scope :purchased_by, lambda { |user| joins(:order_items).joins(:orders).where(:orders => {:user_id => user.try(:id), :purchase_state => EffectiveOrders::PURCHASED}).uniq }
27
+ scope :sold, -> { purchased() }
28
+ scope :sold_by, lambda { |user| joins(:order_items).joins(:orders).where(:order_items => {:seller_id => user.try(:id)}).where(:orders => {:purchase_state => EffectiveOrders::PURCHASED}).uniq }
29
+
30
+ scope :not_purchased, -> { where('id NOT IN (?)', purchased.pluck(:id).presence || [0]) }
31
+ end
32
+
33
+ module ClassMethods
34
+ def after_purchase(&block)
35
+ send :define_method, :after_purchase do |order, order_item| self.instance_exec(order, order_item, &block) end
36
+ end
37
+
38
+ def after_decline(&block)
39
+ send :define_method, :after_decline do |order, order_item| self.instance_exec(order, order_item, &block) end
40
+ end
41
+ end
42
+
43
+ # Regular instance methods
44
+ def is_effectively_purchasable?
45
+ true
46
+ end
47
+
48
+ def price
49
+ self[:price] || 0
50
+ end
51
+
52
+ # If I have a column type of Integer, and I'm passed a non-Integer, convert it here
53
+ def price=(value)
54
+ integer_column = ((column_for_attribute('price').try(:type) rescue nil) == :integer) # Rails built in method to lookup datatype
55
+
56
+ if integer_column == false
57
+ super
58
+ elsif value.kind_of?(Integer)
59
+ super
60
+ elsif value.kind_of?(String) && !value.include?('.') # Looks like an integer
61
+ super
62
+ else # Could be Float, BigDecimal, or String like 9.99
63
+ super((value.to_f * 100.0).to_i)
64
+ end
65
+ end
66
+
67
+ def title
68
+ self[:title] || 'ActsAsPurchasable'
69
+ end
70
+
71
+ def tax_exempt
72
+ self[:tax_exempt] || false
73
+ end
74
+
75
+ def tax_rate
76
+ @tax_rate ||= (
77
+ self.instance_exec(self, &EffectiveOrders.tax_rate_method).to_f.tap do |rate|
78
+ raise ArgumentError.new("expected EffectiveOrders.tax_rate_method to return a value between 0 and 1. Received #{rate}. Please return 0.05 for 5% tax.") if (rate > 1.0 || rate < 0.0)
79
+ end
80
+ )
81
+ end
82
+
83
+ def seller
84
+ if EffectiveOrders.stripe_connect_enabled
85
+ raise 'acts_as_purchasable object requires the seller be defined to return the User selling this item. This is only a requirement when using StripeConnect.'
86
+ end
87
+ end
88
+
89
+ def purchased?
90
+ @is_purchased ||= orders.any? { |order| order.purchased? }
91
+ end
92
+
93
+ def purchased_by?(user)
94
+ orders.any? { |order| order.purchased? && order.user_id == user.id }
95
+ end
96
+
97
+ def purchased_orders
98
+ orders.select { |order| order.purchased? }
99
+ end
100
+
101
+ def quantity_enabled?
102
+ self.respond_to?(:quantity_enabled) ? quantity_enabled == true : false
103
+ end
104
+
105
+ def quantity_remaining
106
+ (quantity_max - quantity_purchased) rescue 0
107
+ end
108
+
109
+ def sold_out?
110
+ quantity_enabled? ? (quantity_remaining == 0) : false
111
+ end
112
+
113
+ def purchased!(order = nil, order_item = nil)
114
+ # begin
115
+ # self.quantity_purchased = (self.quantity_purchased + 1)
116
+ # rescue
117
+ # end
118
+
119
+ after_purchase(order, order_item) if self.respond_to?(:after_purchase)
120
+ self.save!
121
+ end
122
+
123
+ def declined!(order = nil, order_item = nil)
124
+ after_decline(order, order_item) if self.respond_to?(:after_decline)
125
+ self.save!
126
+ end
127
+
128
+ # Override me if this is a digital purchase.
129
+ def purchased_download_url
130
+ false
131
+ end
132
+
133
+ end
134
+
@@ -0,0 +1,17 @@
1
+ unless defined?(Effective::AccessDenied)
2
+ module Effective
3
+ class AccessDenied < StandardError
4
+ attr_reader :action, :subject
5
+
6
+ def initialize(message = nil, action = nil, subject = nil)
7
+ @message = message
8
+ @action = action
9
+ @subject = subject
10
+ end
11
+
12
+ def to_s
13
+ @message || I18n.t(:'unauthorized.default', :default => 'Access Denied')
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,65 @@
1
+ module Effective
2
+ class Cart < ActiveRecord::Base
3
+ self.table_name = EffectiveOrders.carts_table_name.to_s
4
+
5
+ belongs_to :user # This is optional. We want to let non-logged-in people have carts too
6
+ has_many :cart_items, :inverse_of => :cart, :dependent => :delete_all
7
+
8
+ structure do
9
+ timestamps
10
+ end
11
+
12
+ default_scope -> { includes(:cart_items => :purchasable) }
13
+
14
+ def add(item, quantity = 1)
15
+ raise 'expecting an acts_as_purchasable object' unless item.respond_to?(:is_effectively_purchasable?)
16
+
17
+ existing_item = cart_items.where(:purchasable_id => item.id, :purchasable_type => item.class.name).first
18
+
19
+ if item.quantity_enabled? && (quantity + (existing_item.quantity rescue 0)) > item.quantity_remaining
20
+ raise EffectiveOrders::SoldOutException, "#{item.title} is sold out"
21
+ return
22
+ end
23
+
24
+ if existing_item.present?
25
+ existing_item.update_attributes(:quantity => existing_item.quantity + quantity)
26
+ else
27
+ cart_items.create(:cart => self, :purchasable_id => item.id, :purchasable_type => item.class.name, :quantity => quantity)
28
+ end
29
+ end
30
+ alias_method :add_to_cart, :add
31
+
32
+ def remove(obj)
33
+ (cart_items.find(cart_item) || cart_item).try(:destroy)
34
+ end
35
+ alias_method :remove_from_cart, :remove
36
+
37
+ def includes?(item)
38
+ find(item).present?
39
+ end
40
+
41
+ def find(item)
42
+ cart_items.to_a.find { |cart_item| cart_item == item || cart_item.purchasable == item }
43
+ end
44
+
45
+ def size
46
+ cart_items.size
47
+ end
48
+
49
+ def empty?
50
+ size == 0
51
+ end
52
+
53
+ def subtotal
54
+ cart_items.map(&:subtotal).sum
55
+ end
56
+
57
+ def tax
58
+ cart_items.map(&:tax).sum
59
+ end
60
+
61
+ def total
62
+ cart_items.map(&:total).sum
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,40 @@
1
+ module Effective
2
+ class CartItem < ActiveRecord::Base
3
+ self.table_name = EffectiveOrders.cart_items_table_name.to_s
4
+
5
+ belongs_to :cart
6
+ belongs_to :purchasable, :polymorphic => true
7
+
8
+ structure do
9
+ quantity :integer, :validates => [:presence]
10
+ timestamps
11
+ end
12
+
13
+ validates_presence_of :purchasable
14
+
15
+ delegate :title, :tax_exempt, :tax_rate, :to => :purchasable
16
+
17
+ default_scope -> { order(:updated_at) }
18
+
19
+ def price
20
+ if (purchasable.price || 0).kind_of?(Integer)
21
+ purchasable.price || 0
22
+ else
23
+ ActiveSupport::Deprecation.warn('price is a non-integer. It should be an Integer representing the number of cents. Continuing with (price * 100.0).floor conversion') unless EffectiveOrders.silence_deprecation_warnings
24
+ (purchasable.price * 100.0).floor rescue 0
25
+ end
26
+ end
27
+
28
+ def subtotal
29
+ price * quantity
30
+ end
31
+
32
+ def tax
33
+ tax_exempt ? 0 : (subtotal * tax_rate).ceil
34
+ end
35
+
36
+ def total
37
+ subtotal + tax
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,61 @@
1
+ module Effective
2
+ class Customer < ActiveRecord::Base
3
+ self.table_name = EffectiveOrders.customers_table_name.to_s
4
+
5
+ attr_accessor :token # This is a convenience method so we have a place to store StripeConnect temporary access tokens
6
+
7
+ belongs_to :user
8
+ has_many :subscriptions, :inverse_of => :customer
9
+
10
+ structure do
11
+ stripe_customer_id :string # cus_xja7acoa03
12
+ stripe_active_card :string # **** **** **** 4242 Visa 05/12
13
+ stripe_connect_access_token :string # If using StripeConnect and this user is a connected Seller
14
+
15
+ timestamps
16
+ end
17
+
18
+ validates_presence_of :user
19
+ validates_uniqueness_of :user_id # Only 1 customer per user may exist
20
+
21
+ scope :customers, -> { where("#{EffectiveOrders.customers_table_name.to_s}.stripe_customer_id IS NOT NULL") }
22
+
23
+ class << self
24
+ def for_user(user)
25
+ if user.present?
26
+ Effective::Customer.where(:user_id => (user.try(:id) rescue user.to_i)).first_or_create
27
+ end
28
+ end
29
+ end
30
+
31
+ def stripe_customer
32
+ @stripe_customer ||= if stripe_customer_id.present?
33
+ ::Stripe::Customer.retrieve(stripe_customer_id)
34
+ else
35
+ ::Stripe::Customer.create(:email => user.email, :description => user.id.to_s).tap do |stripe_customer|
36
+ self.update_attributes(:stripe_customer_id => stripe_customer.id)
37
+ end
38
+ end
39
+ end
40
+
41
+ def update_card!(token)
42
+ if token.present? # Oh, so they want to use a new credit card...
43
+ stripe_customer.card = token # This sets the default_card to the new card
44
+
45
+ if stripe_customer.save && stripe_customer.default_card.present?
46
+ card = stripe_customer.cards.retrieve(stripe_customer.default_card)
47
+
48
+ self.stripe_active_card = "**** **** **** #{card.last4} #{card.type} #{card.exp_month}/#{card.exp_year}"
49
+ self.save!
50
+ else
51
+ raise Exception.new('unable to update stripe customer with new card')
52
+ end
53
+ end
54
+ end
55
+
56
+ def is_stripe_connect_seller?
57
+ stripe_connect_access_token.present?
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,45 @@
1
+ if defined?(EffectiveDatatables)
2
+ module Effective
3
+ module Datatables
4
+ class Customers < Effective::Datatable
5
+ table_column :email, :column => 'users.email' do |user|
6
+ mail_to user.email, user.email
7
+ end
8
+
9
+ if EffectiveOrders.stripe_enabled
10
+ table_column :stripe_customer_id
11
+ table_column :stripe_active_card
12
+ end
13
+
14
+ if EffectiveOrders.stripe_connect_enabled
15
+ table_column :stripe_connect_access_token
16
+ end
17
+
18
+ table_column :subscription_types, :column => 'subscription_types'
19
+
20
+ table_column :actions, :sortable => false, :filter => false, :partial => '/admin/customers/actions'
21
+
22
+ def collection
23
+ Effective::Customer.customers.uniq
24
+ .joins(:user)
25
+ .joins(:subscriptions)
26
+ .select('customers.*')
27
+ .select('users.email AS email')
28
+ .select("array_to_string(array(#{Effective::Subscription.purchased.select('subscriptions.stripe_plan_id').where('subscriptions.customer_id = customers.id').to_sql}), ' ,') AS subscription_types")
29
+ .group('customers.id')
30
+ .group('subscriptions.stripe_plan_id')
31
+ .group('users.email')
32
+ end
33
+
34
+ def search_column(collection, table_column, search_term)
35
+ if table_column[:name] == 'subscription_types'
36
+ collection.where('subscriptions.stripe_plan_id ILIKE ?', "%#{search_term}%")
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,53 @@
1
+ if defined?(EffectiveDatatables)
2
+ module Effective
3
+ module Datatables
4
+ class Orders < Effective::Datatable
5
+ table_column :id do |order|
6
+ order.to_param
7
+ end
8
+
9
+ array_column :email, :label => 'Buyer', :if => Proc.new { attributes[:user_id].blank? } do |order|
10
+ link_to order.user.email, (edit_admin_user_path(order.user) rescue admin_user_path(order.user) rescue '#')
11
+ end
12
+
13
+ array_column :order_items do |order|
14
+ content_tag(:ul) do
15
+ order.order_items.map { |oi| content_tag(:li, oi.title) }.join().html_safe
16
+ end
17
+ end
18
+
19
+ table_column :purchased_at
20
+
21
+ array_column :total do |order|
22
+ price_to_currency(order.total)
23
+ end
24
+
25
+ table_column :actions, :sortable => false, :filter => false do |order|
26
+ content_tag(:span, :style => 'white-space: nowrap;') do
27
+ [
28
+ link_to('View', (datatables_admin_path? ? effective_orders.admin_order_path(order) : effective_orders.order_path(order))),
29
+ (link_to('Resend Receipt', effective_orders.resend_buyer_receipt_path(order), {'data-confirm' => 'This action will resend a copy of the original email receipt. Send receipt now?'}) if order.try(:purchased?))
30
+ ].compact.join(' - ').html_safe
31
+ end
32
+ end
33
+
34
+ def collection
35
+ if attributes[:user_id].present?
36
+ Effective::Order.purchased.where(:user_id => attributes[:user_id]).includes(:user).includes(:order_items)
37
+ else
38
+ Effective::Order.purchased.includes(:user).includes(:order_items)
39
+ end
40
+ end
41
+
42
+ def search_column(collection, table_column, search_term)
43
+ if table_column[:name] == 'id'
44
+ collection.where(:id => search_term)
45
+ else
46
+ super
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+ end