effective_orders 4.5.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 (135) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +1004 -0
  4. data/app/assets/images/effective_orders/stripe.png +0 -0
  5. data/app/assets/javascripts/effective_orders.js +6 -0
  6. data/app/assets/javascripts/effective_orders/customers.js.coffee +32 -0
  7. data/app/assets/javascripts/effective_orders/providers/stripe.js.coffee +77 -0
  8. data/app/assets/javascripts/effective_orders/subscriptions.js.coffee +81 -0
  9. data/app/assets/stylesheets/effective_orders.scss +2 -0
  10. data/app/assets/stylesheets/effective_orders/_cart.scss +4 -0
  11. data/app/assets/stylesheets/effective_orders/_order.scss +58 -0
  12. data/app/controllers/admin/customers_controller.rb +24 -0
  13. data/app/controllers/admin/order_items_controller.rb +16 -0
  14. data/app/controllers/admin/orders_controller.rb +223 -0
  15. data/app/controllers/effective/carts_controller.rb +85 -0
  16. data/app/controllers/effective/concerns/purchase.rb +62 -0
  17. data/app/controllers/effective/customers_controller.rb +20 -0
  18. data/app/controllers/effective/orders_controller.rb +162 -0
  19. data/app/controllers/effective/providers/cheque.rb +22 -0
  20. data/app/controllers/effective/providers/free.rb +33 -0
  21. data/app/controllers/effective/providers/mark_as_paid.rb +33 -0
  22. data/app/controllers/effective/providers/moneris.rb +60 -0
  23. data/app/controllers/effective/providers/paypal.rb +33 -0
  24. data/app/controllers/effective/providers/phone.rb +22 -0
  25. data/app/controllers/effective/providers/pretend.rb +26 -0
  26. data/app/controllers/effective/providers/refund.rb +33 -0
  27. data/app/controllers/effective/providers/stripe.rb +72 -0
  28. data/app/controllers/effective/subscripter_controller.rb +18 -0
  29. data/app/controllers/effective/webhooks_controller.rb +109 -0
  30. data/app/datatables/admin/effective_customers_datatable.rb +22 -0
  31. data/app/datatables/admin/effective_orders_datatable.rb +100 -0
  32. data/app/datatables/effective_orders_datatable.rb +79 -0
  33. data/app/helpers/effective_carts_helper.rb +113 -0
  34. data/app/helpers/effective_orders_helper.rb +143 -0
  35. data/app/helpers/effective_paypal_helper.rb +49 -0
  36. data/app/helpers/effective_stripe_helper.rb +85 -0
  37. data/app/helpers/effective_subscriptions_helper.rb +34 -0
  38. data/app/mailers/effective/orders_mailer.rb +196 -0
  39. data/app/models/concerns/acts_as_purchasable.rb +118 -0
  40. data/app/models/concerns/acts_as_subscribable.rb +90 -0
  41. data/app/models/concerns/acts_as_subscribable_buyer.rb +49 -0
  42. data/app/models/effective/access_denied.rb +17 -0
  43. data/app/models/effective/cart.rb +88 -0
  44. data/app/models/effective/cart_item.rb +40 -0
  45. data/app/models/effective/customer.rb +92 -0
  46. data/app/models/effective/order.rb +541 -0
  47. data/app/models/effective/order_item.rb +63 -0
  48. data/app/models/effective/product.rb +23 -0
  49. data/app/models/effective/sold_out_validator.rb +7 -0
  50. data/app/models/effective/subscripter.rb +185 -0
  51. data/app/models/effective/subscription.rb +95 -0
  52. data/app/models/effective/tax_rate_calculator.rb +48 -0
  53. data/app/views/admin/customers/_actions.html.haml +2 -0
  54. data/app/views/admin/customers/index.html.haml +6 -0
  55. data/app/views/admin/customers/show.html.haml +6 -0
  56. data/app/views/admin/order_items/index.html.haml +3 -0
  57. data/app/views/admin/orders/_datatable_actions.html.haml +18 -0
  58. data/app/views/admin/orders/_form.html.haml +35 -0
  59. data/app/views/admin/orders/_form_note_internal.html.haml +7 -0
  60. data/app/views/admin/orders/_order_actions.html.haml +9 -0
  61. data/app/views/admin/orders/_order_item_fields.html.haml +14 -0
  62. data/app/views/admin/orders/checkout.html.haml +3 -0
  63. data/app/views/admin/orders/edit.html.haml +6 -0
  64. data/app/views/admin/orders/index.html.haml +6 -0
  65. data/app/views/admin/orders/new.html.haml +4 -0
  66. data/app/views/admin/orders/show.html.haml +4 -0
  67. data/app/views/effective/carts/_cart.html.haml +28 -0
  68. data/app/views/effective/carts/_cart_actions.html.haml +3 -0
  69. data/app/views/effective/carts/show.html.haml +17 -0
  70. data/app/views/effective/customers/_customer.html.haml +72 -0
  71. data/app/views/effective/customers/_form.html.haml +21 -0
  72. data/app/views/effective/customers/edit.html.haml +4 -0
  73. data/app/views/effective/customers/update.js.erb +5 -0
  74. data/app/views/effective/orders/_checkout_actions.html.haml +3 -0
  75. data/app/views/effective/orders/_checkout_step1.html.haml +4 -0
  76. data/app/views/effective/orders/_checkout_step2.html.haml +37 -0
  77. data/app/views/effective/orders/_datatable_actions.html.haml +2 -0
  78. data/app/views/effective/orders/_fields.html.haml +31 -0
  79. data/app/views/effective/orders/_fields_note.html.haml +7 -0
  80. data/app/views/effective/orders/_fields_terms.html.haml +8 -0
  81. data/app/views/effective/orders/_order.html.haml +11 -0
  82. data/app/views/effective/orders/_order_actions.html.haml +18 -0
  83. data/app/views/effective/orders/_order_deferred.html.haml +9 -0
  84. data/app/views/effective/orders/_order_footer.html.haml +1 -0
  85. data/app/views/effective/orders/_order_header.html.haml +23 -0
  86. data/app/views/effective/orders/_order_items.html.haml +72 -0
  87. data/app/views/effective/orders/_order_notes.html.haml +17 -0
  88. data/app/views/effective/orders/_order_payment.html.haml +24 -0
  89. data/app/views/effective/orders/_order_shipping.html.haml +30 -0
  90. data/app/views/effective/orders/_orders_table.html.haml +23 -0
  91. data/app/views/effective/orders/cheque/_form.html.haml +4 -0
  92. data/app/views/effective/orders/declined.html.haml +12 -0
  93. data/app/views/effective/orders/deferred.html.haml +13 -0
  94. data/app/views/effective/orders/deferred/_form.html.haml +16 -0
  95. data/app/views/effective/orders/edit.html.haml +3 -0
  96. data/app/views/effective/orders/free/_form.html.haml +5 -0
  97. data/app/views/effective/orders/index.html.haml +3 -0
  98. data/app/views/effective/orders/mark_as_paid/_form.html.haml +23 -0
  99. data/app/views/effective/orders/moneris/_form.html.haml +47 -0
  100. data/app/views/effective/orders/new.html.haml +3 -0
  101. data/app/views/effective/orders/paypal/_form.html.haml +5 -0
  102. data/app/views/effective/orders/phone/_form.html.haml +4 -0
  103. data/app/views/effective/orders/pretend/_form.html.haml +8 -0
  104. data/app/views/effective/orders/purchased.html.haml +11 -0
  105. data/app/views/effective/orders/refund/_form.html.haml +5 -0
  106. data/app/views/effective/orders/show.html.haml +6 -0
  107. data/app/views/effective/orders/stripe/_element.html.haml +8 -0
  108. data/app/views/effective/orders/stripe/_form.html.haml +31 -0
  109. data/app/views/effective/orders_mailer/order_error.html.haml +11 -0
  110. data/app/views/effective/orders_mailer/order_receipt_to_admin.html.haml +2 -0
  111. data/app/views/effective/orders_mailer/order_receipt_to_buyer.html.haml +2 -0
  112. data/app/views/effective/orders_mailer/payment_request_to_buyer.html.haml +13 -0
  113. data/app/views/effective/orders_mailer/pending_order_invoice_to_buyer.html.haml +13 -0
  114. data/app/views/effective/orders_mailer/refund_notification_to_admin.html.haml +15 -0
  115. data/app/views/effective/orders_mailer/subscription_canceled.html.haml +9 -0
  116. data/app/views/effective/orders_mailer/subscription_created.html.haml +13 -0
  117. data/app/views/effective/orders_mailer/subscription_event_to_admin.html.haml +13 -0
  118. data/app/views/effective/orders_mailer/subscription_payment_failed.html.haml +9 -0
  119. data/app/views/effective/orders_mailer/subscription_payment_succeeded.html.haml +9 -0
  120. data/app/views/effective/orders_mailer/subscription_trial_expired.html.haml +5 -0
  121. data/app/views/effective/orders_mailer/subscription_trialing.html.haml +7 -0
  122. data/app/views/effective/orders_mailer/subscription_updated.html.haml +13 -0
  123. data/app/views/effective/subscripter/_form.html.haml +60 -0
  124. data/app/views/effective/subscripter/_plan.html.haml +23 -0
  125. data/app/views/layouts/effective_orders_mailer_layout.html.haml +25 -0
  126. data/config/effective_orders.rb +279 -0
  127. data/config/routes.rb +70 -0
  128. data/db/migrate/01_create_effective_orders.rb.erb +137 -0
  129. data/lib/effective_orders.rb +243 -0
  130. data/lib/effective_orders/engine.rb +60 -0
  131. data/lib/effective_orders/version.rb +3 -0
  132. data/lib/generators/effective_orders/install_generator.rb +63 -0
  133. data/lib/generators/templates/effective_orders_mailer_preview.rb +120 -0
  134. data/lib/tasks/effective_orders_tasks.rake +69 -0
  135. metadata +276 -0
@@ -0,0 +1,6 @@
1
+ //= require effective_addresses
2
+
3
+ //= require ./effective_orders/customers
4
+ //= require ./effective_orders/subscriptions
5
+ //= require_tree ./effective_orders/providers
6
+
@@ -0,0 +1,32 @@
1
+ stripeCustomerChangeCardHandler = (key, form) ->
2
+ StripeCheckout.configure
3
+ key: key
4
+ closed: -> EffectiveForm.reset(form) unless form.hasClass('stripe-success')
5
+ token: (token, args) ->
6
+ if token.error
7
+ message = "An error ocurred when contacting Stripe. Your card has not been charged. Please refresh the page and try again. #{token.error.message}"
8
+
9
+ form.removeClass('stripe-success')
10
+ form.find('.invalid-feedback').html(message).show()
11
+ alert(message)
12
+ else
13
+ form.find("input[name$='[stripe_token]']").val('' + token['id'])
14
+ form.addClass('stripe-success').submit() # Submits the form. As this is a remote form, submits via JS
15
+
16
+ # When we click 'Change credit card', make sure the form collects a credit card
17
+ $(document).on 'click', ".effective-orders-stripe-update-card[type='submit']", (event) ->
18
+ $form = $(event.currentTarget).closest('form')
19
+
20
+ # Get stripe data payload
21
+ stripe = $form.data('stripe')
22
+ return unless stripe?
23
+
24
+ # Okay, we're good to call stripe
25
+ event.preventDefault()
26
+ EffectiveForm.submitting($form) # Disable and spin while we popup stripe
27
+
28
+ stripeCustomerChangeCardHandler(stripe.key, $form).open
29
+ image: stripe.image
30
+ name: stripe.name
31
+ email: stripe.email
32
+ panelLabel: 'Update Card Details'
@@ -0,0 +1,77 @@
1
+ this.StripeForm ||= class StripeForm
2
+ constructor: ->
3
+ @form = null
4
+ @paymentIntent = null
5
+
6
+ @data = null
7
+ @stripe = null
8
+ @card = null
9
+
10
+ initialize: ->
11
+ @form = $('form[data-stripe-form]').first()
12
+ return false unless @form.length > 0
13
+
14
+ @paymentIntent = @form.find("input[name$='[payment_intent_id]']").first()
15
+ @data = @form.data('stripe-form')
16
+ @stripe = Stripe(@data.key)
17
+ @card = @stripe.elements().create('card', @style())
18
+
19
+ @mount()
20
+
21
+ style: ->
22
+ style: {
23
+ base: { color: '#32325d', fontSize: '16px', },
24
+ invalid: { color: '#dc3545', iconColor: '#dc3545' }
25
+ }
26
+
27
+ mount: ->
28
+ @card.mount('#stripe-card-element')
29
+
30
+ @card.addEventListener('change', (error) ->
31
+ $('#stripe-card-errors').text(if error.message then error.message else '')
32
+ )
33
+
34
+ @form.on('click', '[type=submit]', (event) => @submit(event))
35
+
36
+ submit: (event) ->
37
+ event.preventDefault()
38
+
39
+ payment = if @shouldUseNewCard() then @useNewCard() else @useExistingCard()
40
+
41
+ $.active = $.active + 1
42
+
43
+ payment.then((result) =>
44
+ if result.error
45
+ @errorPayment(result.error)
46
+ else if result.paymentIntent.status == 'succeeded'
47
+ @submitPayment(result.paymentIntent)
48
+ ).then((result) =>
49
+ $.active = $.active - 1
50
+ )
51
+
52
+ shouldUseNewCard: ->
53
+ @form.get(0).checkValidity() &&
54
+ @paymentIntent.val().length == 0 &&
55
+ (@data.token_required || @form.children('.collapse.show').length > 0)
56
+
57
+ useNewCard: ->
58
+ @stripe.confirmCardPayment(@data.client_secret, {
59
+ payment_method: {
60
+ card: @card,
61
+ billing_details: { email: @data.email }
62
+ },
63
+ setup_future_usage: 'off_session'
64
+ })
65
+
66
+ useExistingCard: ->
67
+ @stripe.confirmCardPayment(@data.client_secret, { payment_method: @data.payment_method })
68
+
69
+ submitPayment: (payment) ->
70
+ @paymentIntent.val(payment['id'])
71
+ @form.submit()
72
+
73
+ errorPayment: (error) ->
74
+ $('#stripe-card-errors').text(error.message)
75
+ EffectiveForm.invalidate(@form);
76
+
77
+ $ -> (new StripeForm()).initialize()
@@ -0,0 +1,81 @@
1
+ stripeSubscriptionHandler = (key, form) ->
2
+ StripeCheckout.configure
3
+ key: key
4
+ closed: -> EffectiveForm.reset(form) unless form.hasClass('stripe-success')
5
+ token: (token, args) ->
6
+ if token.error
7
+ message = "An error ocurred when contacting Stripe. Your card has not been charged. Your plan has not changed. Please refresh the page and try again. #{token.error.message}"
8
+
9
+ form.removeClass('stripe-success')
10
+ form.find('.invalid-feedback').html(message).show()
11
+ alert(message)
12
+ else
13
+ form.find("input[name$='[stripe_token]']").val('' + token['id'])
14
+ form.addClass('stripe-success').submit() # Submits the form. As this is a remote form, submits via JS
15
+
16
+ # This updates the form whenever a quantity change is made
17
+ $(document).on 'change keyup', '.effective-orders-subscripter-plan-quantity', (event) ->
18
+ $obj = $(event.currentTarget)
19
+ $plan = $obj.closest('.effective-orders-stripe-plan')
20
+ return unless $plan.length == 1
21
+
22
+ # Assign the quantity to each quantity field
23
+ $plan.closest('form')
24
+ .find(".effective-orders-stripe-plan:not([data-plan-id='#{$plan.data('id')}'])")
25
+ .find("input[name$='[quantity]']").val($obj.val())
26
+
27
+ quantity = $obj.val() || 0
28
+
29
+ $plan.closest('form').find(".effective-orders-stripe-plan").each ->
30
+ # Assign all totals
31
+ plan = $(this)
32
+ amount = parseInt(plan.data('amount'))
33
+ interval = plan.data('interval')
34
+
35
+ total = (quantity * amount)
36
+ total = '$' + (total / 100.0).toFixed(2)
37
+
38
+ plan.find('#effective_subscripter_total_amount').text(total)
39
+
40
+ # Assign savings if present
41
+ savings = parseInt(plan.data('savings'))
42
+
43
+ if savings > 0
44
+ total_savings = '$' + ((quantity * savings) / 100.0).toFixed(2)
45
+ plan.find('.subscripter-total-savings').find('span').text(total_savings)
46
+
47
+ # Hijack submit and get a stripe token
48
+ $(document).on 'click', ".effective-orders-stripe-token-required[type='submit'],[data-choose-stripe-plan-id]", (event) ->
49
+ $obj = $(event.currentTarget)
50
+ $form = $obj.closest('form')
51
+
52
+ # Get stripe data payload
53
+ stripe = $form.data('stripe')
54
+ return unless stripe?
55
+
56
+ plans = $form.data('plans')
57
+ return unless plans?
58
+
59
+ # If we're doing choose button mode
60
+ if $obj.data('choose-stripe-plan-id')
61
+ $form.find("input[name$='[stripe_plan_id]']").val($obj.data('choose-stripe-plan-id'))
62
+ return true unless $obj.hasClass('effective-orders-stripe-token-required')
63
+
64
+ # Make sure there is a plan selected
65
+ selected_plan_id = $form.find("input[name$='[stripe_plan_id]']:checked").val() || $form.find("input[name$='[stripe_plan_id]']").val() || ''
66
+ return unless selected_plan_id.length > 0
67
+
68
+ # Match plan
69
+ plan = plans.find (plan, _) => plan.id == selected_plan_id
70
+ return unless plan?
71
+
72
+ # Okay, we're good to call stripe
73
+ event.preventDefault()
74
+ EffectiveForm.submitting($form) # Disable and spin while we popup stripe
75
+
76
+ stripeSubscriptionHandler(stripe.key, $form).open
77
+ image: stripe.image
78
+ name: stripe.name
79
+ email: stripe.email
80
+ description: plan.name
81
+ panelLabel: 'Update Plan'
@@ -0,0 +1,2 @@
1
+ @import 'effective_orders/cart';
2
+ @import 'effective_orders/order';
@@ -0,0 +1,4 @@
1
+ .effective-cart {
2
+ .subtotal { text-align: right; }
3
+ }
4
+
@@ -0,0 +1,58 @@
1
+ .effective-order {
2
+ table {
3
+ clear: both;
4
+ margin-bottom: 25px;
5
+
6
+ tfoot {
7
+ > tr:first-child {
8
+ border-top: 20px solid transparent;
9
+ }
10
+
11
+ th {
12
+ border: none;
13
+ text-align: right;
14
+ padding-right: 65px;
15
+ }
16
+
17
+ td {
18
+ text-align: right;
19
+ }
20
+
21
+ .actions { border: none; }
22
+ }
23
+ }
24
+
25
+ .price { text-align: right; }
26
+ }
27
+
28
+ // Print - Resend Receipt. Ontop of order.
29
+ .effective-order-actions {
30
+ text-align: right;
31
+ margin-bottom: 0.5rem;
32
+ }
33
+
34
+ .effective-order-change-items {
35
+ margin-bottom: 1rem;
36
+ }
37
+
38
+ .effective-order-admin-purchase-actions {
39
+ margin-top: 0.5rem;
40
+ }
41
+
42
+ form.new_effective_order {
43
+ .checkbox { margin-top: 30px; }
44
+ .remove-nested-fields { margin-top: 30px; }
45
+ a.remove_fields.dynamic { float: right; }
46
+ }
47
+
48
+ @media print {
49
+ .effective-orders-page-content { display: none; }
50
+
51
+ .effective-order-actions { display: none; }
52
+ .effective-order-purchase-actions { display: none; }
53
+ .effective-order-admin-purchase-actions { display: none; }
54
+
55
+ .effective-heading { display: none; }
56
+ .effective-admin-heading { display: none; }
57
+ .effective-order-internal-note-form { display: none; }
58
+ }
@@ -0,0 +1,24 @@
1
+ module Admin
2
+ class CustomersController < ApplicationController
3
+ before_action :authenticate_user!
4
+
5
+ layout (EffectiveOrders.layout.kind_of?(Hash) ? EffectiveOrders.layout[:admin_customers] : EffectiveOrders.layout)
6
+
7
+ def index
8
+ @datatable = Admin::EffectiveCustomersDatatable.new(self)
9
+
10
+ @page_title = 'Customers'
11
+
12
+ EffectiveOrders.authorize!(self, :admin, :effective_orders)
13
+ EffectiveOrders.authorize!(self, :index, Effective::Customer)
14
+ end
15
+
16
+ def show
17
+ @customer = Effective::Customer.find(params[:id])
18
+
19
+ @page_title ||= @customer.to_s
20
+ EffectiveOrders.authorize!(self, :show, Effective::Customer)
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ module Admin
2
+ class OrderItemsController < ApplicationController
3
+ before_action :authenticate_user!
4
+
5
+ layout (EffectiveOrders.layout.kind_of?(Hash) ? EffectiveOrders.layout[:admin_orders] : EffectiveOrders.layout)
6
+
7
+ def index
8
+ @datatable = Admin::EffectiveOrderItemsDatatable.new(self)
9
+
10
+ @page_title = 'Order Items'
11
+
12
+ EffectiveOrders.authorize!(self, :admin, :effective_orders)
13
+ EffectiveOrders.authorize!(self, :index, Effective::OrderItem)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,223 @@
1
+ module Admin
2
+ class OrdersController < ApplicationController
3
+ before_action :authenticate_user!
4
+
5
+ layout (EffectiveOrders.layout.kind_of?(Hash) ? EffectiveOrders.layout[:admin_orders] : EffectiveOrders.layout)
6
+
7
+ def new
8
+ @order = Effective::Order.new
9
+
10
+ if params[:user_id]
11
+ @order.user = User.where(id: params[:user_id]).first
12
+ end
13
+
14
+ if params[:duplicate_id]
15
+ @duplicate = Effective::Order.deep.find(params[:duplicate_id])
16
+ EffectiveOrders.authorize!(self, :show, @duplicate)
17
+
18
+ @order.add(@duplicate)
19
+ end
20
+
21
+ @page_title = 'New Order'
22
+
23
+ raise 'please install cocoon gem to use this page' unless defined?(Cocoon)
24
+
25
+ authorize_effective_order!
26
+ end
27
+
28
+ def create
29
+ @user = User.find_by_id(order_params[:user_id])
30
+ @order = Effective::Order.new(user: @user)
31
+
32
+ authorize_effective_order!
33
+ error = nil
34
+
35
+ Effective::Order.transaction do
36
+ begin
37
+ (order_params[:order_items_attributes] || {}).each do |_, item_attrs|
38
+ purchasable = Effective::Product.new(item_attrs[:purchasable_attributes])
39
+ @order.add(purchasable, quantity: item_attrs[:quantity])
40
+ end
41
+
42
+ @order.attributes = order_params.except(:order_items_attributes, :user_id)
43
+ @order.pending!
44
+
45
+ message = 'Successfully created order'
46
+ message << ". A request for payment has been sent to #{@order.emails_send_to}" if @order.send_payment_request_to_buyer?
47
+ flash[:success] = message
48
+
49
+ redirect_to(admin_redirect_path) and return
50
+ rescue => e
51
+ error = e.message
52
+ raise ActiveRecord::Rollback
53
+ end
54
+ end
55
+
56
+ @page_title = 'New Order'
57
+ flash.now[:danger] = flash_danger(@order)
58
+ render :new
59
+ end
60
+
61
+ def edit
62
+ @order = Effective::Order.find(params[:id])
63
+ @page_title ||= @order.to_s
64
+
65
+ authorize_effective_order!
66
+ end
67
+
68
+ def update
69
+ @order = Effective::Order.find(params[:id])
70
+
71
+ @page_title ||= @order.to_s
72
+
73
+ authorize_effective_order!
74
+
75
+ Effective::Order.transaction do
76
+ begin
77
+ @order.assign_attributes(order_params)
78
+ @order.save!
79
+ redirect_to(admin_redirect_path) and return
80
+ rescue => e
81
+ raise ActiveRecord::Rollback
82
+ end
83
+ end
84
+
85
+ flash.now[:danger] = "Unable to update order: #{@order.errors.full_messages.to_sentence}"
86
+ render :edit
87
+ end
88
+
89
+ def show
90
+ @order = Effective::Order.find(params[:id])
91
+
92
+ @page_title ||= @order.to_s
93
+
94
+ authorize_effective_order!
95
+ end
96
+
97
+ # The show page posts to this action
98
+ # See Effective::OrdersController checkout
99
+ def checkout
100
+ @order = Effective::Order.find(params[:id])
101
+
102
+ authorize_effective_order!
103
+
104
+ @page_title ||= 'Checkout'
105
+
106
+ if request.get?
107
+ @order.assign_confirmed_if_valid!
108
+ render :checkout and return
109
+ end
110
+
111
+ Effective::Order.transaction do
112
+ begin
113
+ @order.assign_attributes(checkout_params)
114
+ @order.confirm!
115
+ redirect_to(effective_orders.checkout_admin_order_path(@order)) and return
116
+ rescue => e
117
+ raise ActiveRecord::Rollback
118
+ end
119
+ end
120
+
121
+ flash.now[:danger] = "Unable to proceed: #{flash_errors(@order)}. Please try again."
122
+ render :checkout
123
+ end
124
+
125
+ def index
126
+ @datatable = Admin::EffectiveOrdersDatatable.new(self)
127
+
128
+ @page_title = 'Orders'
129
+
130
+ authorize_effective_order!
131
+ end
132
+
133
+ def destroy
134
+ @order = Effective::Order.all.not_purchased.find(params[:id])
135
+
136
+ authorize_effective_order!
137
+
138
+ if @order.destroy
139
+ flash[:success] = 'Successfully deleted order'
140
+ else
141
+ flash[:danger] = "Unable to delete order: #{@order.errors.full_messages.to_sentence}"
142
+ end
143
+
144
+ redirect_to(effective_orders.admin_orders_path)
145
+ end
146
+
147
+ def send_payment_request
148
+ @order = Effective::Order.pending.find(params[:id])
149
+ authorize_effective_order!
150
+
151
+ if @order.send_payment_request_to_buyer!
152
+ flash[:success] = "A request for payment has been sent to #{@order.emails_send_to}"
153
+ else
154
+ flash[:danger] = 'Unable to send payment request'
155
+ end
156
+
157
+ if respond_to?(:redirect_back)
158
+ redirect_back(fallback_location: effective_orders.admin_order_path(@order))
159
+ elsif request.referrer.present?
160
+ redirect_to :back
161
+ else
162
+ redirect_to effective_orders.admin_order_path(@order)
163
+ end
164
+ end
165
+
166
+ def bulk_send_payment_request
167
+ @orders = Effective::Order.pending.where(id: params[:ids])
168
+
169
+ begin
170
+ authorize_effective_order!
171
+
172
+ @orders.each { |order| order.send_payment_request_to_buyer! }
173
+ render json: { status: 200, message: "Successfully sent #{@orders.length} payment request emails"}
174
+ rescue => e
175
+ render json: { status: 500, message: "Bulk send payment request error: #{e.message}" }
176
+ end
177
+ end
178
+
179
+ private
180
+
181
+ def order_params
182
+ params.require(:effective_order).permit(:user_id, :cc,
183
+ :send_payment_request_to_buyer, :note_internal, :note_to_buyer,
184
+ :payment_provider, :payment_card, :payment, :send_mark_as_paid_email_to_buyer,
185
+ order_items_attributes: [
186
+ :quantity, :_destroy, purchasable_attributes: [
187
+ :name, :price, :tax_exempt
188
+ ]
189
+ ]
190
+ )
191
+ end
192
+
193
+ def checkout_params
194
+ params.require(:effective_order).permit(EffectiveOrders.permitted_params)
195
+ end
196
+
197
+ def authorize_effective_order!
198
+ EffectiveOrders.authorize!(self, :admin, :effective_orders)
199
+ EffectiveOrders.authorize!(self, action_name.to_sym, @order || Effective::Order)
200
+ end
201
+
202
+ def admin_redirect_path
203
+ # Allow an app to define effective_orders_admin_redirect_path in their ApplicationController
204
+ path = if self.respond_to?(:effective_orders_admin_redirect_path)
205
+ effective_orders_admin_redirect_path(params[:commit], @order)
206
+ end
207
+
208
+ return path if path.present?
209
+
210
+ case params[:commit].to_s
211
+ when 'Save' ; effective_orders.admin_order_path(@order)
212
+
213
+ when 'Continue' ; effective_orders.admin_orders_path
214
+ when 'Add New' ; effective_orders.new_admin_order_path(user_id: @order.user.try(:to_param))
215
+ when 'Duplicate' ; effective_orders.new_admin_order_path(duplicate_id: @order.to_param)
216
+ when 'Checkout' ; effective_orders.checkout_admin_order_path(@order.to_param)
217
+
218
+ else effective_orders.admin_order_path(@order)
219
+ end
220
+ end
221
+
222
+ end
223
+ end