effective_orders 4.0.0beta1 → 4.0.0beta2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e0cf749b4cef2660e59b16b802211c75e79e7400
4
- data.tar.gz: c4c452a35a548596146b2fdf099916065361dd55
3
+ metadata.gz: da1a756900ec2369535464ac59524e04e480c05f
4
+ data.tar.gz: 593c2b74776dd66558b67efc5ddc2fb09b94203b
5
5
  SHA512:
6
- metadata.gz: af34590308483e7db60eb579b952167622809d2c0cfb9c1101221800e0a3ce51e5ffe6d8d7895755f7fe18282ac379e6de0406f47802c445e16a2492fd838a7a
7
- data.tar.gz: c872866386ab0490fd154cb8caf59119c75ea61cb2f601049d92a75ec0d9a97a7f4e939563a13807b53f3440fe8a0d636c40326da2aa6b89f2858a4fa321f73e
6
+ metadata.gz: d3d7d27ebe782735240eba20c79372d281c9ffa009305c853e2bdd6ed319cf19936051ce8e82b9859f3ee4f6265e67db02161c984b51e42f229b3d4fb2e2474e
7
+ data.tar.gz: a8ff40a48e296934603d9841df3a4854d36fe4ce4ffa59d896310a6176aae905b29aab7ca7b4e1ca2d35bdbd75846b3eeea2c6f2b96e6737410c57d80b42f89c
data/README.md CHANGED
@@ -533,10 +533,10 @@ The one gotcha with the above two scenarios, is that when `purchase!` is called,
533
533
  - `validates_numericality_of :total, greater_than_or_equal_to: minimum_charge` where minimum_charge is the configured value, once again from the initializer
534
534
  - `validates_presence_of :order_items` the Order must have at least one OrderItem
535
535
 
536
- You can skip validations with the following command, but be careful as this skips all validations:
536
+ You can skip some buyer validations with the following command:
537
537
 
538
538
  ```ruby
539
- Effective::Order.new(@product, user: @user).purchase!(validate: false)
539
+ Effective::Order.new(@product, user: @user).purchase!(skip_buyer_validations: true)
540
540
  ```
541
541
 
542
542
  The `@product` is now considered purchased.
@@ -940,136 +940,6 @@ The secret can be any string. Here's a good way to come up with a secret:
940
940
  This process should be very similar although you'll create and configure a seller account on paypal.com rather than the sandbox site.
941
941
  You should generate separate private and public certificates/keys for this and it is advisable to not keep production certificates/keys in version control.
942
942
 
943
- ## Paying via CCBill
944
-
945
- Effective Orders has implemented checkout with CCBill using their "Dynamic Pricing" API and does not
946
- integrate with CCBill subscriptions. If you need to make payments for CCBill subscriptions, please help
947
- by improving Effective Orders with this functionality.
948
-
949
- ### CCBill Account Setup
950
-
951
- You need a merchant account with CCBill so go sign up and login. To set up your account with Dynamic
952
- Pricing, you'll need to go through some hoops with CCBill:
953
-
954
- 1. Get approval to operate a merchant account from CCBill Merchant Services (they'll need to see your
955
- site in action)
956
- 2. Provide CCBill with two valid forms of ID and wait for them to approve your account
957
- 3. Create two additional sub accounts for a staging server and your localhost
958
- ("Account Info" > "Account Setup")
959
- 4. Get each new sub account approved by Merchant Services (mention that they are for testing only)
960
- 5. Ask CCBill tech support to set up all sub accounts with Dynamic Pricing
961
- 6. Set the postback urls for each sub account to be `"#{ your_domain_name }/orders/ccbill_postback`
962
- (Look for 'Approval Post URL' and 'Denial Post URL' in "Account Info" > "Sub Account Admin" > select sub account
963
- \> "Advanced" > under 'Background Post Information')
964
-
965
- ### Effective Orders Configuration
966
-
967
- Get the following information and add it
968
- to the Effective Orders initializer. CCBill live chat is generally quick and helpful. They can help you
969
- find any of this information.
970
-
971
- - Account number (`:client_accnum`)
972
- - Subaccount number (`:client_subacc`)
973
- - Checkout form id/name (`:form_name`)
974
- - Currency code (`:currency_code`)
975
- - Encryption key/salt (`:dynamic_pricing_salt`)("Account Info" > "Sub Account Admin" > select sub account
976
- \> "Advanced" > under 'Upgrade Security Setup Information' > 'Encryption key')
977
-
978
- Effective Orders will authorize to make changes to the customer's order during CCBill's post back after
979
- the transaction. Since, this is not an action that the customer takes directly, please make sure
980
- that the Effective Orders authorization method returns `true` for the controller/action
981
- `'Effective::OrdersController#ccbill_postback'` with an `Effective::Order` resource.
982
-
983
- ### Testing transactions with CCBill
984
-
985
- To test payments with CCBill:
986
-
987
- 1. Set up yourself as a user who is authorized to make test transactions
988
- ([See this guide](https://www.ccbill.com/cs/wiki/tiki-index.php?page=How+do+I+set+up+a+user+to+process+test+transactions%3F))
989
- 2. Use ngrok on localhost or a staging server to go through a normal payment (remember to configure the
990
- postback urls (see #6 of [CCBill Account Setup](#ccbill-account-setup))
991
- 3. Use one of the provided credit card numbers in the guide from step 1 for the associated response
992
-
993
- ### Helpful CCBill Documentation
994
-
995
- - [Dynamic Pricing](https://www.ccbill.com/cs/wiki/tiki-index.php?page=Dynamic+Pricing)
996
- - [Dynamic Pricing User Guide](https://www.ccbill.com/cs/wiki/tiki-index.php?page=Dynamic+Pricing+User+Guide)
997
- - [Background Post](https://www.ccbill.com/cs/wiki/tiki-index.php?page=Background+Post)
998
- - [Webhooks](https://www.ccbill.com/cs/wiki/tiki-index.php?page=Webhooks+User+Guide)
999
- - [How do I set up a user to process test transactions?](https://www.ccbill.com/cs/wiki/tiki-index.php?page=How+do+I+set+up+a+user+to+process+test+transactions%3F)
1000
-
1001
- ## Paying Using App Currency or Logic
1002
-
1003
- There are situations when you want to handle payment logic inside your application. For example, an app could
1004
- have it's own type of currency (tokens, points, kudos) that could be used to make payments.
1005
-
1006
- Let's look at a sample app checkout configuration to see how to get this kind of checkout working:
1007
-
1008
- ```ruby
1009
- config.app_checkout_enabled = true
1010
-
1011
- config.app_checkout = {
1012
- checkout_label: 'Checkout with Tokens',
1013
- service: TokenCheckoutService,
1014
- declined_flash: "Payment was unsuccessful. Please try again."
1015
- }
1016
- ```
1017
-
1018
- First, decide on a checkout button label (this is only used when there's more than one checkout option available).
1019
- Other checkout buttons follow the pattern of "Checkout with \_\_\_", like "Checkout with Moneris".
1020
-
1021
- Second, create a service object in your app and add a reference to it here ([see below for details](#the-app-checkout-service-object)).
1022
-
1023
- The last configuration option is the declined flash message displayed when the `successful?` method
1024
- returns `false`.
1025
-
1026
- Finally, *the app checkout button is hidden* unless effective orders receives authorzation to
1027
- display it. This is helpful if certain users don't use the in-app currency or in-app checkout.
1028
- To authorize effective orders and display the button, you should make sure that the effective
1029
- orders `authorization_method`, defined earlier in the config file, returns `true` if the three
1030
- arguments are: An instance of `Effective::OrdersController`, the Symbol `:app_checkout`, and the
1031
- instance of `Effective::Order`.
1032
-
1033
- ### The App Checkout Service Object
1034
-
1035
- The app checkout [service object](http://stevelorek.com/service-objects.html) is responsible for containing
1036
- the businiess logic of in-app payments (i.e. with tokens).
1037
-
1038
- There are two recommended ways to implement the service object:
1039
-
1040
- 1. Create a service object that inherits from `EffectiveOrders::AppCheckoutService`
1041
- 2. Use the [interactor gem](https://github.com/collectiveidea/interactor) (interactor is just another
1042
- term for service object)
1043
-
1044
- Here's a sample service object (and likely the minimal implementation that you'll want):
1045
-
1046
- ```ruby
1047
- # located in /app/services/token_checkout_service.rb
1048
-
1049
- # Instances of this class have access to the Effective::Order object in the instance variable, @order.
1050
- class TokenCheckoutService < EffectiveOrders::AppCheckoutService
1051
- # This method is responsible to complete the payment transaction
1052
- def call
1053
- cost_in_tokens = Token.cost_in_tokens(@order.price)
1054
- @order.user.tokens = @order.user.tokens - cost_in_tokens
1055
- @success = @order.user.save
1056
- end
1057
-
1058
- # Did the purchase finish correctly?
1059
- def successful?
1060
- @success
1061
- end
1062
-
1063
- # - optional -
1064
- # return a Hash or easily serializable object like a String
1065
- #
1066
- # The return value of this method will be serialized and stored on the `payment_details` attribute
1067
- # of the `Effective::Order`.
1068
- def payment_details
1069
- end
1070
- end
1071
- ```
1072
-
1073
943
 
1074
944
  ## License
1075
945
 
@@ -5,9 +5,9 @@ module Effective
5
5
 
6
6
  protected
7
7
 
8
- def order_purchased(provider:, card: 'none', details: 'none', email: true, skip_buyer_validations: false, purchased_url: nil, declined_url: nil)
8
+ def order_purchased(payment:, provider:, card: 'none', email: true, skip_buyer_validations: false, purchased_url: nil, declined_url: nil)
9
9
  begin
10
- @order.purchase!(provider: provider, card: card, details: details, email: email, skip_buyer_validations: skip_buyer_validations)
10
+ @order.purchase!(payment: payment, provider: provider, card: card, email: email, skip_buyer_validations: skip_buyer_validations)
11
11
 
12
12
  Effective::Cart.where(user_id: @order.user_id).destroy_all
13
13
 
@@ -26,8 +26,8 @@ module Effective
26
26
  end
27
27
  end
28
28
 
29
- def order_declined(provider:, card: 'none', details: 'none', message: nil, declined_url: nil)
30
- @order.decline!(provider: provider, card: card, details: details) rescue nil
29
+ def order_declined(payment:, provider:, card: 'none', message: nil, declined_url: nil)
30
+ @order.decline!(payment: payment, provider: provider, card: card) rescue nil
31
31
 
32
32
  flash[:danger] = message.presence || 'Payment was unsuccessful. Your credit card was declined by the payment processor. Please try again.'
33
33
 
@@ -15,7 +15,7 @@ module Effective
15
15
  end
16
16
 
17
17
  order_purchased(
18
- details: 'free order. no payment required.',
18
+ payment: 'free order. no payment required.',
19
19
  provider: 'free',
20
20
  card: 'none',
21
21
  purchased_url: params[:purchased_url],
@@ -12,7 +12,7 @@ module Effective
12
12
  @order.assign_attributes(mark_as_paid_params.except(:payment, :payment_provider, :payment_card))
13
13
 
14
14
  order_purchased(
15
- details: mark_as_paid_params[:payment],
15
+ payment: mark_as_paid_params[:payment],
16
16
  provider: mark_as_paid_params[:payment_provider],
17
17
  card: mark_as_paid_params[:payment_card],
18
18
  email: @order.send_mark_as_paid_email_to_buyer?,
@@ -1,3 +1,5 @@
1
+ require 'net/http'
2
+
1
3
  module Effective
2
4
  module Providers
3
5
  module Moneris
@@ -17,33 +19,40 @@ module Effective
17
19
  declined_url = params.delete(:rvar_declined_url)
18
20
 
19
21
  if @order.purchased? # Fallback to a success condition of the Order is already purchased
20
- order_purchased(details: params, provider: 'moneris', card: params[:card], purchased_url: purchased_url)
21
- return
22
+ return order_purchased(payment: params, provider: 'moneris', card: params[:card], purchased_url: purchased_url)
22
23
  end
23
24
 
24
- if params[:result].to_s == '1' && params[:transactionKey].present?
25
- verify_params = parse_moneris_response(send_moneris_verify_request(params[:transactionKey])) || {}
25
+ # Invalid Result
26
+ if params[:result].to_s != '1' || params[:transactionKey].blank?
27
+ return order_declined(payment: params, provider: 'moneris', card: params[:card], declined_url: declined_url)
28
+ end
26
29
 
27
- response_code = verify_params[:response_code].to_i # Sometimes moneris sends us the string 'null'
30
+ # Verify response from moneris
31
+ payment = params.merge(verify_moneris_transaction(params[:transactionKey]))
32
+ valid = (1..49).include?(payment[:response_code].to_i) # Must be > 0 and < 50 to be valid. Sometimes we get the string 'null'
28
33
 
29
- if response_code > 0 && response_code < 50 # Less than 50 means a successful validation
30
- order_purchased(details: params.merge(verify_params), provider: 'moneris', card: params[:card], purchased_url: purchased_url)
31
- else
32
- order_declined(details: params.merge(verify_params), provider: 'moneris', card: params[:card], declined_url: declined_url)
33
- end
34
- else
35
- order_declined(details: params, provider: 'moneris', card: params[:card], declined_url: declined_url)
34
+ if valid == false
35
+ return order_declined(payment: payment, provider: 'moneris', card: params[:card], declined_url: declined_url)
36
36
  end
37
+
38
+ order_purchased(payment: payment, provider: 'moneris', card: params[:card], purchased_url: purchased_url)
37
39
  end
38
40
 
39
41
  private
40
42
 
41
- def parse_moneris_response(text)
42
- text.split("<br>").inject(Hash.new()) { |h, i| h[i.split(' ').first.to_sym] = i.split(' ').last ; h } rescue {response: text}
43
- end
43
+ def verify_moneris_transaction(transactionKey)
44
+ # Send a verification POST request
45
+ uri = URI.parse(EffectiveOrders.moneris[:verify_url])
46
+ params = { ps_store_id: EffectiveOrders.moneris[:ps_store_id], hpp_key: EffectiveOrders.moneris[:hpp_key], transactionKey: transactionKey }
47
+ headers = { 'Referer': effective_orders.moneris_postback_orders_url }
48
+
49
+ http = Net::HTTP.new(uri.host, uri.port)
50
+ http.use_ssl = true
51
+
52
+ body = http.post(uri.path, params.to_query, headers).body
44
53
 
45
- def send_moneris_verify_request(verify_key)
46
- `curl -F ps_store_id='#{EffectiveOrders.moneris[:ps_store_id]}' -F hpp_key='#{EffectiveOrders.moneris[:hpp_key]}' -F transactionKey='#{verify_key}' --referer #{effective_orders.moneris_postback_orders_url} #{EffectiveOrders.moneris[:verify_url]}`
54
+ # Parse response into a Hash
55
+ body.split('<br>').inject({}) { |h, i| h[i.split(' ').first.to_sym] = i.split(' ').last; h }
47
56
  end
48
57
 
49
58
  end
@@ -16,11 +16,11 @@ module Effective
16
16
 
17
17
  if @order.present?
18
18
  if @order.purchased?
19
- order_purchased(details: params, provider: 'paypal', card: params[:payment_type])
19
+ order_purchased(payment: params, provider: 'paypal', card: params[:payment_type])
20
20
  elsif (params[:payment_status] == 'Completed' && params[:custom] == EffectiveOrders.paypal[:secret])
21
- order_purchased(details: params, provider: 'paypal', card: params[:payment_type])
21
+ order_purchased(payment: params, provider: 'paypal', card: params[:payment_type])
22
22
  else
23
- order_declined(details: params, provider: 'paypal', card: params[:payment_type])
23
+ order_declined(payment: params, provider: 'paypal', card: params[:payment_type])
24
24
  end
25
25
  end
26
26
 
@@ -9,7 +9,7 @@ module Effective
9
9
  EffectiveOrders.authorize!(self, :update, @order)
10
10
 
11
11
  order_purchased(
12
- details: 'for pretend',
12
+ payment: 'for pretend',
13
13
  provider: 'pretend',
14
14
  card: 'none',
15
15
  purchased_url: params[:purchased_url],
@@ -18,7 +18,7 @@ module Effective
18
18
  @order.assign_attributes(refund_params.except(:payment, :payment_provider, :payment_card))
19
19
 
20
20
  order_purchased(
21
- details: refund_params[:payment],
21
+ payment: refund_params[:payment],
22
22
  provider: refund_params[:payment_provider],
23
23
  card: refund_params[:payment_card],
24
24
  email: @order.send_mark_as_paid_email_to_buyer?,
@@ -15,7 +15,7 @@ module Effective
15
15
 
16
16
  if @stripe_charge.valid? && (response = process_stripe_charge(@stripe_charge)) != false
17
17
  order_purchased(
18
- details: response,
18
+ payment: response,
19
19
  provider: 'stripe',
20
20
  card: (response[:charge]['source']['brand'] rescue nil)
21
21
  )
@@ -35,7 +35,7 @@ module EffectiveCartsHelper
35
35
  label: 'My Cart',
36
36
  id: 'current_cart',
37
37
  rel: :nofollow,
38
- class: 'btn btn-default'
38
+ class: 'btn btn-secondary'
39
39
  }.merge(opts)
40
40
 
41
41
  label = options.delete(:label)
@@ -97,6 +97,12 @@ module Effective
97
97
  end
98
98
  end
99
99
 
100
+ with_options if: -> { confirmed? && !skip_buyer_validations? } do |order|
101
+ if EffectiveOrders.terms_and_conditions
102
+ order.validates :terms_and_conditions, presence: true
103
+ end
104
+ end
105
+
100
106
  # When Purchased
101
107
  with_options if: -> { purchased? } do |order|
102
108
  order.validates :purchased_at, presence: true
@@ -132,24 +138,22 @@ module Effective
132
138
  return unless atts.present?
133
139
 
134
140
  if atts.kind_of?(Hash)
135
- if (keywords = (atts.keys - [:item, :items, :user, :billing_address, :shipping_address])).present?
136
- raise ArgumentError.new("unknown keyword: #{keywords.join(' ')}")
137
- end
141
+ items = Array(atts.delete(:item)) + Array(atts.delete(:items))
138
142
 
139
- items = Array(atts[:item]) + Array(atts[:items])
143
+ self.user = atts.delete(:user) || (items.first.user if items.first.respond_to?(:user))
140
144
 
141
- self.user = atts[:user] || (items.first.user if items.first.respond_to?(:user))
142
-
143
- if atts.key?(:billing_address)
144
- self.billing_address = atts[:billing_address]
145
+ if (address = atts.delete(:billing_address)).present?
146
+ self.billing_address = address
145
147
  self.billing_address.full_name ||= user.to_s.presence
146
148
  end
147
149
 
148
- if atts.key?(:shipping_address)
149
- self.shipping_address = atts[:shipping_address]
150
+ if (address = atts.delete(:shipping_address)).present?
151
+ self.shipping_address = address
150
152
  self.shipping_address.full_name ||= user.to_s.presence
151
153
  end
152
154
 
155
+ atts.each { |key, value| self.send("#{key}=", value) }
156
+
153
157
  add(items) if items.present?
154
158
  else # Attributes are not a Hash
155
159
  self.user = atts.user if atts.respond_to?(:user)
@@ -304,42 +308,35 @@ module Effective
304
308
  false
305
309
  end
306
310
 
307
- # Effective::Order.new(Product.first, user: User.first).purchase!(details: 'manual purchase')
308
- # order.purchase!(details: {key: value})
309
- def purchase!(details: 'none', provider: 'none', card: 'none', validate: true, email: true, skip_buyer_validations: false)
311
+ # Effective::Order.new(items: Product.first, user: User.first).purchase!(email: false)
312
+ def purchase!(payment: 'none', provider: 'none', card: 'none', note: nil, email: true, skip_buyer_validations: false)
310
313
  return false if purchased?
311
-
312
- success = false
313
314
  error = nil
314
315
 
316
+ assign_attributes(
317
+ state: EffectiveOrders::PURCHASED,
318
+ purchased_at: Time.zone.now,
319
+ payment: payment_to_h(payment),
320
+ payment_provider: provider,
321
+ payment_card: (card.presence || 'none'),
322
+ skip_buyer_validations: skip_buyer_validations
323
+ )
324
+
315
325
  Effective::Order.transaction do
316
326
  begin
317
- self.state = EffectiveOrders::PURCHASED
318
- self.purchased_at ||= Time.zone.now
319
-
320
- self.payment = details.kind_of?(Hash) ? details : { details: details.to_s }
321
- self.payment_provider = provider.to_s
322
- self.payment_card = card.to_s.presence || 'none'
323
-
324
- self.skip_buyer_validations = skip_buyer_validations
325
-
326
- assign_purchased_order_to_purchasables
327
-
328
327
  run_purchasable_callbacks(:before_purchase)
329
-
330
- save!(validate: validate)
331
-
332
- success = true
328
+ save!
329
+ update_purchasables_purchased_order!
333
330
  rescue => e
334
331
  self.state = state_was
335
- self.purchased_at = purchased_at_was
332
+ self.purchased_at = nil
336
333
 
337
334
  error = e.message
338
335
  raise ::ActiveRecord::Rollback
339
336
  end
340
337
  end
341
338
 
342
- raise "Failed to purchase order: #{error || errors.full_messages.to_sentence}" unless success
339
+ raise "Failed to purchase order: #{error || errors.full_messages.to_sentence}" unless error.nil?
343
340
 
344
341
  send_order_receipts! if email
345
342
 
@@ -348,36 +345,34 @@ module Effective
348
345
  true
349
346
  end
350
347
 
351
- def decline!(details: 'none', provider: 'none', card: 'none', validate: true)
348
+ def decline!(payment: 'none', provider: 'none', card: 'none', validate: true)
352
349
  return false if declined?
353
350
 
354
351
  raise EffectiveOrders::AlreadyPurchasedException.new('order already purchased') if purchased?
355
352
 
356
- success = false
357
353
  error = nil
358
354
 
355
+ assign_attributes(
356
+ state: EffectiveOrders::DECLINED,
357
+ purchased_at: nil,
358
+ payment: payment_to_h(payment),
359
+ payment_provider: provider,
360
+ payment_card: (card.presence || 'none'),
361
+ skip_buyer_validations: true
362
+ )
363
+
359
364
  Effective::Order.transaction do
360
365
  begin
361
- self.state = EffectiveOrders::DECLINED
362
- self.purchased_at = nil
363
-
364
- self.payment = details.kind_of?(Hash) ? details : { details: details.to_s }
365
- self.payment_provider = provider.to_s
366
- self.payment_card = card.to_s.presence || 'none'
367
-
368
366
  save!(validate: validate)
369
-
370
- success = true
371
367
  rescue => e
372
368
  self.state = state_was
373
- self.purchased_at = purchased_at_was
374
369
 
375
370
  error = e.message
376
371
  raise ::ActiveRecord::Rollback
377
372
  end
378
373
  end
379
374
 
380
- raise "Failed to decline order: #{error || errors.full_messages.to_sentence}" unless success
375
+ raise "Failed to decline order: #{error || errors.full_messages.to_sentence}" unless error.nil?
381
376
 
382
377
  run_purchasable_callbacks(:after_decline)
383
378
 
@@ -452,8 +447,8 @@ module Effective
452
447
  end
453
448
  end
454
449
 
455
- def assign_purchased_order_to_purchasables
456
- order_items.each { |oi| oi.purchasable.purchased_order = self }
450
+ def update_purchasables_purchased_order!
451
+ order_items.each { |oi| oi.purchasable.update_column(:purchased_order_id, self.id) }
457
452
  end
458
453
 
459
454
  def run_purchasable_callbacks(name)
@@ -472,5 +467,15 @@ module Effective
472
467
  end
473
468
  end
474
469
 
470
+ def payment_to_h(payment)
471
+ if payment.respond_to?(:to_unsafe_h)
472
+ payment.to_unsafe_h.to_h
473
+ elsif payment.respond_to?(:to_h)
474
+ payment.to_h
475
+ else
476
+ { details: (payment.to_s.presence || 'none') }
477
+ end
478
+ end
479
+
475
480
  end
476
481
  end
@@ -16,7 +16,7 @@ module Effective
16
16
  # timestamps
17
17
 
18
18
  validates :purchasable, associated: true, presence: true
19
- accepts_nested_attributes_for :purchasable, allow_destroy: false, reject_if: :all_blank, update_only: true
19
+ accepts_nested_attributes_for :purchasable
20
20
 
21
21
  validates :title, presence: true
22
22
  validates :quantity, presence: true, numericality: { greater_than: 0 }
@@ -34,6 +34,10 @@ module Effective
34
34
  price * quantity
35
35
  end
36
36
 
37
+ def quantity
38
+ self[:quantity] || 1
39
+ end
40
+
37
41
  def tax
38
42
  return 0 if tax_exempt?
39
43
  raise 'parent Effective::Order must have a tax_rate to compute order item tax' unless order.try(:tax_rate).present?
@@ -1,7 +1,9 @@
1
1
  = dropdown(variation: :dropleft) do
2
2
  - if datatable.admin_namespace?
3
3
  = dropdown_link_to 'View', effective_orders.admin_order_path(order)
4
- = dropdown_link_to 'Edit', effective_orders.edit_admin_order_path(order)
4
+
5
+ - if order.purchased? == false
6
+ = dropdown_link_to 'Edit', effective_orders.edit_admin_order_path(order)
5
7
 
6
8
  - if order.purchased?
7
9
  = dropdown_link_to 'Email receipt to buyer', effective_orders.send_buyer_receipt_order_path(order),
@@ -2,6 +2,11 @@
2
2
 
3
3
  %p= flash[:danger]
4
4
 
5
- %p Your #{link_to_current_cart(label: 'Cart')} items have been saved. Please try again.
6
-
7
-
5
+ %p.effective-orders-page-content
6
+ = link_to 'Home', root_path, class: 'btn btn-primary'
7
+
8
+ %p.effective-orders-page-content
9
+ - if current_cart.present?
10
+ Your #{link_to_current_cart(label: 'Cart')} items have been saved. Please try again.
11
+ - else
12
+ Please try again.
@@ -139,8 +139,8 @@ module EffectiveOrders
139
139
  ('pretend' if pretend?),
140
140
  ('stripe' if stripe?),
141
141
  ('credit card' if mark_as_paid?),
142
- ('none' if mark_as_paid?),
143
142
  ('other' if mark_as_paid?),
143
+ 'none'
144
144
  ].compact
145
145
  end
146
146
 
@@ -1,3 +1,3 @@
1
1
  module EffectiveOrders
2
- VERSION = '4.0.0beta1'.freeze
2
+ VERSION = '4.0.0beta2'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_orders
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0beta1
4
+ version: 4.0.0beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-20 00:00:00.000000000 Z
11
+ date: 2018-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails