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 +4 -4
- data/README.md +2 -132
- data/app/controllers/effective/concerns/purchase.rb +4 -4
- data/app/controllers/effective/providers/free.rb +1 -1
- data/app/controllers/effective/providers/mark_as_paid.rb +1 -1
- data/app/controllers/effective/providers/moneris.rb +26 -17
- data/app/controllers/effective/providers/paypal.rb +3 -3
- data/app/controllers/effective/providers/pretend.rb +1 -1
- data/app/controllers/effective/providers/refund.rb +1 -1
- data/app/controllers/effective/providers/stripe.rb +1 -1
- data/app/helpers/effective_carts_helper.rb +1 -1
- data/app/models/effective/order.rb +52 -47
- data/app/models/effective/order_item.rb +5 -1
- data/app/views/admin/orders/_actions.html.haml +3 -1
- data/app/views/effective/orders/declined.html.haml +8 -3
- data/lib/effective_orders.rb +1 -1
- data/lib/effective_orders/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da1a756900ec2369535464ac59524e04e480c05f
|
4
|
+
data.tar.gz: 593c2b74776dd66558b67efc5ddc2fb09b94203b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
536
|
+
You can skip some buyer validations with the following command:
|
537
537
|
|
538
538
|
```ruby
|
539
|
-
Effective::Order.new(@product, user: @user).purchase!(
|
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',
|
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,
|
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',
|
30
|
-
@order.decline!(provider: provider, card: card
|
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
|
|
@@ -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
|
-
|
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(
|
21
|
-
return
|
22
|
+
return order_purchased(payment: params, provider: 'moneris', card: params[:card], purchased_url: purchased_url)
|
22
23
|
end
|
23
24
|
|
24
|
-
|
25
|
-
|
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
|
-
|
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
|
-
|
30
|
-
|
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
|
42
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
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(
|
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(
|
21
|
+
order_purchased(payment: params, provider: 'paypal', card: params[:payment_type])
|
22
22
|
else
|
23
|
-
order_declined(
|
23
|
+
order_declined(payment: params, provider: 'paypal', card: params[:payment_type])
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -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
|
-
|
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
|
-
|
18
|
+
payment: response,
|
19
19
|
provider: 'stripe',
|
20
20
|
card: (response[:charge]['source']['brand'] rescue nil)
|
21
21
|
)
|
@@ -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
|
-
|
136
|
-
raise ArgumentError.new("unknown keyword: #{keywords.join(' ')}")
|
137
|
-
end
|
141
|
+
items = Array(atts.delete(:item)) + Array(atts.delete(:items))
|
138
142
|
|
139
|
-
|
143
|
+
self.user = atts.delete(:user) || (items.first.user if items.first.respond_to?(:user))
|
140
144
|
|
141
|
-
|
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.
|
149
|
-
self.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!(
|
308
|
-
|
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
|
-
|
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 =
|
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
|
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!(
|
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
|
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
|
456
|
-
order_items.each { |oi| oi.purchasable.
|
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
|
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
|
-
|
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
|
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.
|
data/lib/effective_orders.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2018-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|