effective_orders 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
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