effective_orders 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +856 -0
- data/Rakefile +24 -0
- data/app/assets/images/effective_orders/stripe_connect.png +0 -0
- data/app/assets/javascripts/effective_orders/shipping_address_toggle.js.coffee +30 -0
- data/app/assets/javascripts/effective_orders/stripe_charges.js.coffee +26 -0
- data/app/assets/javascripts/effective_orders/stripe_subscriptions.js.coffee +28 -0
- data/app/assets/javascripts/effective_orders.js +2 -0
- data/app/assets/stylesheets/effective_orders/_order.scss +30 -0
- data/app/assets/stylesheets/effective_orders.css.scss +1 -0
- data/app/controllers/admin/customers_controller.rb +15 -0
- data/app/controllers/admin/orders_controller.rb +22 -0
- data/app/controllers/effective/carts_controller.rb +70 -0
- data/app/controllers/effective/orders_controller.rb +191 -0
- data/app/controllers/effective/providers/moneris.rb +94 -0
- data/app/controllers/effective/providers/paypal.rb +29 -0
- data/app/controllers/effective/providers/stripe.rb +125 -0
- data/app/controllers/effective/providers/stripe_connect.rb +47 -0
- data/app/controllers/effective/subscriptions_controller.rb +123 -0
- data/app/controllers/effective/webhooks_controller.rb +86 -0
- data/app/helpers/effective_carts_helper.rb +90 -0
- data/app/helpers/effective_orders_helper.rb +108 -0
- data/app/helpers/effective_paypal_helper.rb +37 -0
- data/app/helpers/effective_stripe_helper.rb +63 -0
- data/app/mailers/effective/orders_mailer.rb +64 -0
- data/app/models/concerns/acts_as_purchasable.rb +134 -0
- data/app/models/effective/access_denied.rb +17 -0
- data/app/models/effective/cart.rb +65 -0
- data/app/models/effective/cart_item.rb +40 -0
- data/app/models/effective/customer.rb +61 -0
- data/app/models/effective/datatables/customers.rb +45 -0
- data/app/models/effective/datatables/orders.rb +53 -0
- data/app/models/effective/order.rb +247 -0
- data/app/models/effective/order_item.rb +69 -0
- data/app/models/effective/stripe_charge.rb +35 -0
- data/app/models/effective/subscription.rb +95 -0
- data/app/models/inputs/price_field.rb +63 -0
- data/app/models/inputs/price_form_input.rb +7 -0
- data/app/models/inputs/price_formtastic_input.rb +9 -0
- data/app/models/inputs/price_input.rb +19 -0
- data/app/models/inputs/price_simple_form_input.rb +8 -0
- data/app/models/validators/effective/sold_out_validator.rb +7 -0
- data/app/views/active_admin/effective_orders/orders/_show.html.haml +70 -0
- data/app/views/admin/customers/_actions.html.haml +2 -0
- data/app/views/admin/customers/index.html.haml +10 -0
- data/app/views/admin/orders/index.html.haml +7 -0
- data/app/views/admin/orders/show.html.haml +11 -0
- data/app/views/effective/carts/_cart.html.haml +33 -0
- data/app/views/effective/carts/show.html.haml +18 -0
- data/app/views/effective/orders/_checkout_step_1.html.haml +39 -0
- data/app/views/effective/orders/_checkout_step_2.html.haml +18 -0
- data/app/views/effective/orders/_my_purchases.html.haml +15 -0
- data/app/views/effective/orders/_order.html.haml +4 -0
- data/app/views/effective/orders/_order_header.html.haml +21 -0
- data/app/views/effective/orders/_order_items.html.haml +39 -0
- data/app/views/effective/orders/_order_payment_details.html.haml +11 -0
- data/app/views/effective/orders/_order_shipping.html.haml +19 -0
- data/app/views/effective/orders/_order_user_fields.html.haml +10 -0
- data/app/views/effective/orders/checkout.html.haml +3 -0
- data/app/views/effective/orders/declined.html.haml +10 -0
- data/app/views/effective/orders/moneris/_form.html.haml +34 -0
- data/app/views/effective/orders/my_purchases.html.haml +6 -0
- data/app/views/effective/orders/my_sales.html.haml +28 -0
- data/app/views/effective/orders/new.html.haml +4 -0
- data/app/views/effective/orders/paypal/_form.html.haml +5 -0
- data/app/views/effective/orders/purchased.html.haml +10 -0
- data/app/views/effective/orders/show.html.haml +17 -0
- data/app/views/effective/orders/stripe/_form.html.haml +8 -0
- data/app/views/effective/orders/stripe/_subscription_fields.html.haml +7 -0
- data/app/views/effective/orders_mailer/order_receipt_to_admin.html.haml +8 -0
- data/app/views/effective/orders_mailer/order_receipt_to_buyer.html.haml +8 -0
- data/app/views/effective/orders_mailer/order_receipt_to_seller.html.haml +30 -0
- data/app/views/effective/subscriptions/index.html.haml +16 -0
- data/app/views/effective/subscriptions/new.html.haml +10 -0
- data/app/views/effective/subscriptions/show.html.haml +49 -0
- data/config/routes.rb +57 -0
- data/db/migrate/01_create_effective_orders.rb.erb +91 -0
- data/db/upgrade/02_upgrade_effective_orders_from03x.rb.erb +29 -0
- data/db/upgrade/upgrade_price_column_on_table.rb.erb +17 -0
- data/lib/effective_orders/engine.rb +52 -0
- data/lib/effective_orders/version.rb +3 -0
- data/lib/effective_orders.rb +76 -0
- data/lib/generators/effective_orders/install_generator.rb +38 -0
- data/lib/generators/effective_orders/upgrade_from03x_generator.rb +34 -0
- data/lib/generators/effective_orders/upgrade_price_column_generator.rb +34 -0
- data/lib/generators/templates/README +1 -0
- data/lib/generators/templates/effective_orders.rb +210 -0
- data/spec/controllers/carts_controller_spec.rb +143 -0
- data/spec/controllers/moneris_orders_controller_spec.rb +245 -0
- data/spec/controllers/orders_controller_spec.rb +418 -0
- data/spec/controllers/stripe_orders_controller_spec.rb +127 -0
- data/spec/controllers/webhooks_controller_spec.rb +79 -0
- data/spec/dummy/README.rdoc +8 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/product.rb +17 -0
- data/spec/dummy/app/models/product_with_float_price.rb +17 -0
- data/spec/dummy/app/models/user.rb +28 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +31 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +83 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/devise.rb +254 -0
- data/spec/dummy/config/initializers/effective_addresses.rb +15 -0
- data/spec/dummy/config/initializers/effective_orders.rb +22 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/schema.rb +142 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +487 -0
- data/spec/dummy/log/test.log +347 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/helpers/effective_orders_helper_spec.rb +21 -0
- data/spec/models/acts_as_purchasable_spec.rb +107 -0
- data/spec/models/customer_spec.rb +71 -0
- data/spec/models/factories_spec.rb +13 -0
- data/spec/models/order_item_spec.rb +35 -0
- data/spec/models/order_spec.rb +323 -0
- data/spec/models/stripe_charge_spec.rb +39 -0
- data/spec/models/subscription_spec.rb +103 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/support/factories.rb +118 -0
- metadata +387 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# # Attributes
|
|
4
|
+
describe Product do
|
|
5
|
+
let(:user) { FactoryGirl.create(:user) }
|
|
6
|
+
let(:order) { FactoryGirl.create(:order) }
|
|
7
|
+
let(:product) { order.order_items.first.purchasable }
|
|
8
|
+
let(:product_with_float_price) { FactoryGirl.create(:product_with_float_price) }
|
|
9
|
+
|
|
10
|
+
describe 'assumptions' do
|
|
11
|
+
it 'should be effectively purchasable' do
|
|
12
|
+
product.is_effectively_purchasable?.should eq true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe 'purchased' do
|
|
17
|
+
it 'is purchased? when in a purchased Order' do
|
|
18
|
+
order.purchase!('by a test')
|
|
19
|
+
|
|
20
|
+
product.purchased?.should eq true
|
|
21
|
+
product.purchased_orders.include?(order).should eq true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'is returned by the purchased scopes' do
|
|
25
|
+
order.purchase!('by a test')
|
|
26
|
+
|
|
27
|
+
Product.purchased.to_a.include?(product).should eq true
|
|
28
|
+
Product.purchased_by(order.user).to_a.include?(product).should eq true
|
|
29
|
+
|
|
30
|
+
Product.sold.to_a.include?(product).should eq true
|
|
31
|
+
|
|
32
|
+
Product.not_purchased.to_a.include?(product).should eq false
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe 'float prices' do
|
|
37
|
+
it 'should automatically convert float prices to integer' do
|
|
38
|
+
product_with_float_price.price = 20.00
|
|
39
|
+
product_with_float_price.tax_exempt = true
|
|
40
|
+
|
|
41
|
+
order = Effective::Order.new(product_with_float_price, user)
|
|
42
|
+
|
|
43
|
+
order.subtotal.should eq 2000
|
|
44
|
+
order.tax.should eq 0
|
|
45
|
+
order.total.should eq 2000
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'should automatically convert tax floats to integers' do
|
|
49
|
+
product_with_float_price.price = 20.00
|
|
50
|
+
product_with_float_price.tax_exempt = false
|
|
51
|
+
|
|
52
|
+
order = Effective::Order.new(product_with_float_price, user)
|
|
53
|
+
|
|
54
|
+
order.subtotal.should eq 2000
|
|
55
|
+
order.tax.should eq 100
|
|
56
|
+
order.total.should eq 2100
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe 'price=' do
|
|
61
|
+
it 'should accept an integer price' do
|
|
62
|
+
product = Product.new()
|
|
63
|
+
product.price = 1250
|
|
64
|
+
|
|
65
|
+
product.price.should eq 1250
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'should convert a String that looks like an Integer' do
|
|
69
|
+
product = Product.new()
|
|
70
|
+
product.price = '1250'
|
|
71
|
+
|
|
72
|
+
product.price.should eq 1250
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'should convert a String that looks like a Float' do
|
|
76
|
+
product = Product.new()
|
|
77
|
+
product.price = '12.50'
|
|
78
|
+
|
|
79
|
+
product.price.should eq 1250
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'should convert from a Float' do
|
|
83
|
+
product = Product.new()
|
|
84
|
+
product.price = 12.50
|
|
85
|
+
product.price.should eq 1250
|
|
86
|
+
|
|
87
|
+
product.price = Float(12.50)
|
|
88
|
+
product.price.should eq 1250
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'should convert from a BigDecimal' do
|
|
92
|
+
product = Product.new()
|
|
93
|
+
product.price = BigDecimal.new(12.5, 4)
|
|
94
|
+
|
|
95
|
+
product.price.should eq 1250
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'should treat nil as a zero' do
|
|
99
|
+
product = Product.new()
|
|
100
|
+
product.price = nil
|
|
101
|
+
|
|
102
|
+
product.price.should eq 0
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
require 'stripe_mock'
|
|
4
|
+
|
|
5
|
+
# # Attributes
|
|
6
|
+
describe Effective::Customer do
|
|
7
|
+
let(:customer) { FactoryGirl.create(:customer) }
|
|
8
|
+
let(:user) { FactoryGirl.create(:user) }
|
|
9
|
+
|
|
10
|
+
describe 'Customer.for_user' do
|
|
11
|
+
it 'creates a new Customer when passed a new user' do
|
|
12
|
+
c = Effective::Customer.for_user(user)
|
|
13
|
+
|
|
14
|
+
c.kind_of?(Effective::Customer).should eq true
|
|
15
|
+
c.persisted?.should eq true
|
|
16
|
+
c.valid?.should eq true
|
|
17
|
+
c.user.should eq user
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'returns an existing Customer when passed an existing user' do
|
|
21
|
+
c = Effective::Customer.for_user(customer.user)
|
|
22
|
+
c.should eq customer
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe 'Stripe Integration' do
|
|
27
|
+
before { StripeMock.start }
|
|
28
|
+
after { StripeMock.stop }
|
|
29
|
+
|
|
30
|
+
describe '#stripe_customer' do
|
|
31
|
+
it 'creates a new Stripe::Customer if one doesnt exist.' do
|
|
32
|
+
stripe_customer = customer.stripe_customer
|
|
33
|
+
|
|
34
|
+
stripe_customer.kind_of?(::Stripe::Customer).should eq true
|
|
35
|
+
|
|
36
|
+
stripe_customer.email.should eq customer.user.email
|
|
37
|
+
stripe_customer.id.should eq customer.stripe_customer_id
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'retrieves an existing Stripe::Customer if exists' do
|
|
41
|
+
# Test set up. Create the user.
|
|
42
|
+
user = customer.user
|
|
43
|
+
stripe_customer = ::Stripe::Customer.create(:email => user.email, :description => user.id.to_s)
|
|
44
|
+
customer.update_attributes(:stripe_customer_id => stripe_customer.id)
|
|
45
|
+
|
|
46
|
+
customer.reload
|
|
47
|
+
|
|
48
|
+
customer.stripe_customer.id.should eq stripe_customer.id
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe '#update_card' do
|
|
53
|
+
it 'can update the card' do
|
|
54
|
+
customer.update_card!('sometoken').should eq true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'updates stripe_active_card when updating the card' do
|
|
58
|
+
customer.should_receive(:save!).and_return(true)
|
|
59
|
+
customer.update_card!('sometoken').should eq true
|
|
60
|
+
|
|
61
|
+
customer.stripe_active_card.present?.should eq true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# Attributes
|
|
4
|
+
describe 'Factories' do
|
|
5
|
+
let(:factories) { [:user, :customer, :subscription, :address, :product, :cart, :order_item, :order, :purchased_order, :declined_order, :cart_with_subscription, :order_with_subscription] }
|
|
6
|
+
|
|
7
|
+
before { StripeMock.start }
|
|
8
|
+
after { StripeMock.stop }
|
|
9
|
+
|
|
10
|
+
it 'should have all valid factories' do
|
|
11
|
+
factories.each { |factory| FactoryGirl.create(factory).valid?.should eq true }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# # Attributes
|
|
4
|
+
describe Effective::OrderItem do
|
|
5
|
+
let(:order) { FactoryGirl.create(:order) }
|
|
6
|
+
let(:order_item) { FactoryGirl.create(:order_item) }
|
|
7
|
+
|
|
8
|
+
describe 'validations' do
|
|
9
|
+
it 'is invalid without required fields' do
|
|
10
|
+
order_item = Effective::OrderItem.new()
|
|
11
|
+
order_item.valid?.should eq false
|
|
12
|
+
|
|
13
|
+
order_item.errors[:title].present?.should eq true
|
|
14
|
+
order_item.errors[:quantity].present?.should eq true
|
|
15
|
+
order_item.errors[:tax_exempt].present?.should eq true
|
|
16
|
+
order_item.errors[:purchasable].present?.should eq true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe 'scopes' do
|
|
21
|
+
it 'is included in the Sold scope when order is purchased' do
|
|
22
|
+
order.purchase!('from a test')
|
|
23
|
+
(order.order_items.size > 0).should eq true
|
|
24
|
+
|
|
25
|
+
sold_items = Effective::OrderItem.sold.to_a
|
|
26
|
+
sold_items.size.should eq order.order_items.size
|
|
27
|
+
|
|
28
|
+
order.order_items.each { |oi| sold_items.include?(oi).should eq true }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
end
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Effective::Order do
|
|
4
|
+
let(:cart) { FactoryGirl.create(:cart) }
|
|
5
|
+
let(:order) { FactoryGirl.create(:order) }
|
|
6
|
+
let(:user) { FactoryGirl.create(:user) }
|
|
7
|
+
let(:product) { FactoryGirl.create(:product) }
|
|
8
|
+
let(:product2) { FactoryGirl.create(:product) }
|
|
9
|
+
|
|
10
|
+
it 'calculates dollars based on its order items' do
|
|
11
|
+
order.total.should eq order.order_items.collect(&:total).sum
|
|
12
|
+
order.subtotal.should eq order.order_items.collect(&:subtotal).sum
|
|
13
|
+
order.tax.should eq order.order_items.collect(&:tax).sum
|
|
14
|
+
order.num_items.should eq order.order_items.collect(&:quantity).sum
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe '#initialize' do
|
|
18
|
+
it 'creates appropriate OrderItems when initialized with a Cart' do
|
|
19
|
+
order = Effective::Order.new(cart)
|
|
20
|
+
|
|
21
|
+
order.order_items.each_with_index do |order_item, x|
|
|
22
|
+
order_item.title.should eq cart.cart_items[x].title
|
|
23
|
+
order_item.price.should eq cart.cart_items[x].price
|
|
24
|
+
order_item.quantity.should eq cart.cart_items[x].quantity
|
|
25
|
+
order_item.purchasable.should eq cart.cart_items[x].purchasable
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
order.order_items.length.should eq cart.cart_items.length
|
|
29
|
+
order.total.should eq cart.total
|
|
30
|
+
order.subtotal.should eq cart.subtotal
|
|
31
|
+
order.tax.should eq cart.tax
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'creates appropriate OrderItems when initialized with an array of purchasables' do
|
|
35
|
+
order = Effective::Order.new([product, product2])
|
|
36
|
+
order.order_items.size.should eq 2
|
|
37
|
+
|
|
38
|
+
order.subtotal.should eq (product.price + product2.price)
|
|
39
|
+
order.total.should eq ((product.price + product2.price) * 1.05)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'creates appropriate OrderItems when initialized with a single purchasable' do
|
|
43
|
+
order = Effective::Order.new(product)
|
|
44
|
+
order.order_items.size.should eq 1
|
|
45
|
+
|
|
46
|
+
order_item = order.order_items.first
|
|
47
|
+
|
|
48
|
+
order_item.title.should eq product.title
|
|
49
|
+
order_item.price.should eq product.price
|
|
50
|
+
order_item.purchasable.should eq product
|
|
51
|
+
order_item.quantity.should eq 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'throws an ArgumentError when passed something unexpected' do
|
|
55
|
+
expect { Effective::Order.new(Object.new()) }.to raise_error(ArgumentError)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe 'minimum zero math' do
|
|
60
|
+
it 'has a minimum order total of 0' do
|
|
61
|
+
order.order_items.each { |order_item| order_item.stub(:total).and_return(-1000) }
|
|
62
|
+
|
|
63
|
+
order.order_items.collect(&:total).sum.should eq -3000
|
|
64
|
+
order.total.should eq 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'has no minimum subtotal' do
|
|
68
|
+
order.order_items.each { |order_item| order_item.stub(:subtotal).and_return(-1000) }
|
|
69
|
+
|
|
70
|
+
order.order_items.collect(&:subtotal).sum.should eq -3000
|
|
71
|
+
order.subtotal.should eq -3000
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'has a minimum order tax of 0.00' do
|
|
75
|
+
order.order_items.each { |order_item| order_item.stub(:tax).and_return(-1000) }
|
|
76
|
+
|
|
77
|
+
order.order_items.collect(&:tax).sum.should eq -3000
|
|
78
|
+
order.tax.should eq 0
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe 'user=' do
|
|
83
|
+
it 'assigns the user' do
|
|
84
|
+
order = Effective::Order.new()
|
|
85
|
+
order.user = user
|
|
86
|
+
order.user.should eq user
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'assigns addresses from the user' do
|
|
90
|
+
order = Effective::Order.new()
|
|
91
|
+
|
|
92
|
+
user.billing_address = FactoryGirl.create(:address, :category => :billing)
|
|
93
|
+
user.shipping_address = FactoryGirl.create(:address, :category => :shipping)
|
|
94
|
+
|
|
95
|
+
order.user = user
|
|
96
|
+
|
|
97
|
+
order.billing_address.should eq user.billing_address
|
|
98
|
+
order.save_billing_address.should eq true
|
|
99
|
+
|
|
100
|
+
order.shipping_address.should eq user.shipping_address
|
|
101
|
+
order.save_shipping_address.should eq true
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe 'validations' do
|
|
106
|
+
it 'should be invalid without a user or order_items' do
|
|
107
|
+
order = Effective::Order.new()
|
|
108
|
+
order.valid?.should eq false
|
|
109
|
+
|
|
110
|
+
order.errors[:user_id].present?.should eq true
|
|
111
|
+
order.errors[:order_items].present?.should eq true
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'should be invalid when user is invalid' do
|
|
115
|
+
order.user.stub(:valid?).and_return(false)
|
|
116
|
+
order.valid?.should eq false
|
|
117
|
+
|
|
118
|
+
order.errors[:user].present?.should eq true
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'should be invalid when an order_item is invalid' do
|
|
122
|
+
order.order_items.first.stub(:valid?).and_return(false)
|
|
123
|
+
order.valid?.should eq false
|
|
124
|
+
|
|
125
|
+
order.errors[:order_items].present?.should eq true
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'should be invalid when less than the minimum charge' do
|
|
129
|
+
order.stub(:total).and_return(49)
|
|
130
|
+
|
|
131
|
+
order.valid?.should eq false
|
|
132
|
+
|
|
133
|
+
order.errors[:total].present?.should eq true
|
|
134
|
+
order.errors[:total].first.downcase.include?('minimum order of 50 is required').should eq true
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it 'should be valid when >= minimum charge' do
|
|
138
|
+
order.stub(:total).and_return(50)
|
|
139
|
+
order.valid?.should eq true
|
|
140
|
+
|
|
141
|
+
order.stub(:total).and_return(51)
|
|
142
|
+
order.valid?.should eq true
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'should be valid for a free order' do
|
|
146
|
+
order.order_items.each { |order_item| order_item.stub(:total).and_return(0) }
|
|
147
|
+
|
|
148
|
+
order.valid?.should eq true
|
|
149
|
+
order.errors[:total].present?.should eq false
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe 'purchase!' do
|
|
154
|
+
it 'assigns the purchase_state, purchase_at and payment' do
|
|
155
|
+
order.purchase!('by a test').should eq true
|
|
156
|
+
|
|
157
|
+
order.purchase_state.should eq EffectiveOrders::PURCHASED
|
|
158
|
+
order.purchased_at.kind_of?(Time).should eq true
|
|
159
|
+
order.payment[:details].should eq 'by a test'
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'sends purchased callback to all order_items' do
|
|
163
|
+
order.order_items.each { |oi| oi.purchasable.should_receive(:purchased!).with(order, oi) }
|
|
164
|
+
order.purchase!('by a test')
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'throws an error when purchased twice' do
|
|
168
|
+
order.purchase!('first time')
|
|
169
|
+
|
|
170
|
+
expect { order.purchase!('second time') }.to raise_error(EffectiveOrders::AlreadyPurchasedException)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it 'sends emails to the admin, buyer and seller' do
|
|
174
|
+
Effective::OrdersMailer.deliveries.clear
|
|
175
|
+
|
|
176
|
+
order.purchase!('by a test')
|
|
177
|
+
|
|
178
|
+
Effective::OrdersMailer.deliveries.length.should eq 2
|
|
179
|
+
|
|
180
|
+
Effective::OrdersMailer.deliveries[0].to.first.should eq 'admin@example.com'
|
|
181
|
+
Effective::OrdersMailer.deliveries[0].subject.include?("Order ##{order.to_param} Receipt").should eq true
|
|
182
|
+
|
|
183
|
+
Effective::OrdersMailer.deliveries[1].to.first.should eq order.user.email
|
|
184
|
+
Effective::OrdersMailer.deliveries[1].subject.include?("Order ##{order.to_param} Receipt").should eq true
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it 'does not send email if passed :email => false' do
|
|
188
|
+
Effective::OrdersMailer.deliveries.clear
|
|
189
|
+
|
|
190
|
+
order.purchase!('by a test', :email => false)
|
|
191
|
+
|
|
192
|
+
Effective::OrdersMailer.deliveries.length.should eq 0
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it 'is included with the purchased scope' do
|
|
196
|
+
order.purchase!('by a test')
|
|
197
|
+
Effective::Order.purchased.to_a.include?(order).should eq true
|
|
198
|
+
Effective::Order.purchased_by(order.user).to_a.include?(order).should eq true
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it 'is not included with the declined scope' do
|
|
202
|
+
order.purchase!('by a test')
|
|
203
|
+
Effective::Order.declined.to_a.include?(order).should eq false
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it 'should return false when the Order is invalid' do
|
|
207
|
+
order.stub(:valid?).and_return(false)
|
|
208
|
+
expect { order.purchase!('by a test') }.to raise_exception
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it 'should return true when the Order is invalid and :validate => false is passed' do
|
|
212
|
+
order.stub(:valid?).and_return(false)
|
|
213
|
+
order.purchase!('by a test', :validate => false).should eq true
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
describe 'purchased?' do
|
|
219
|
+
it 'returns true when a purchased order' do
|
|
220
|
+
order.purchase!('by a test')
|
|
221
|
+
order.purchased?.should eq true
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it 'returns false when not purchased' do
|
|
225
|
+
order.purchased?.should eq false
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it 'returns false when declined' do
|
|
229
|
+
order.decline!('by a test')
|
|
230
|
+
order.purchased?.should eq false
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
describe 'decline!' do
|
|
235
|
+
it 'assigns the purchase_state' do
|
|
236
|
+
order.decline!('by a test').should eq true
|
|
237
|
+
|
|
238
|
+
order.purchase_state.should eq EffectiveOrders::DECLINED
|
|
239
|
+
order.payment[:details].should eq 'by a test'
|
|
240
|
+
order.purchased_at.should eq nil
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it 'sends declined callback to all order_items' do
|
|
244
|
+
order.order_items.each { |oi| oi.purchasable.should_receive(:declined!).with(order, oi) }
|
|
245
|
+
order.decline!('by a test')
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it 'throws an error when declined twice' do
|
|
249
|
+
order.decline!('first time')
|
|
250
|
+
|
|
251
|
+
expect { order.decline!('second time') }.to raise_error(EffectiveOrders::AlreadyDeclinedException)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
it 'is included with the declined scope' do
|
|
255
|
+
order.decline!('by a test')
|
|
256
|
+
Effective::Order.declined.to_a.include?(order).should eq true
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it 'is not included with the purchased scope' do
|
|
260
|
+
order.decline!('by a test')
|
|
261
|
+
Effective::Order.purchased.to_a.include?(order).should eq false
|
|
262
|
+
Effective::Order.purchased_by(order.user).to_a.include?(order).should eq false
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
describe 'declined?' do
|
|
267
|
+
it 'returns true when a declined order' do
|
|
268
|
+
order.decline!('by a test')
|
|
269
|
+
order.declined?.should eq true
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
it 'returns false when not declined' do
|
|
273
|
+
order.declined?.should eq false
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it 'returns false when purchased' do
|
|
277
|
+
order.purchase!('by a test')
|
|
278
|
+
order.declined?.should eq false
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
describe '#save_billing_address?' do
|
|
283
|
+
it 'is true when save_billing_address is 1' do
|
|
284
|
+
order.save_billing_address = '1'
|
|
285
|
+
order.save_billing_address?.should eq true
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it 'is false when save_billing_address is 0' do
|
|
289
|
+
order.save_billing_address = '0'
|
|
290
|
+
order.save_billing_address?.should eq false
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it 'is false when save_billing_address is nil' do
|
|
294
|
+
order.save_billing_address = nil
|
|
295
|
+
order.save_billing_address?.should eq false
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
describe '#save_shipping_address?' do
|
|
300
|
+
it 'is true when save_shipping_address is 1' do
|
|
301
|
+
order.save_shipping_address = '1'
|
|
302
|
+
order.save_shipping_address?.should eq true
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
it 'is false when save_shipping_address is 0' do
|
|
306
|
+
order.save_shipping_address = '0'
|
|
307
|
+
order.save_shipping_address?.should eq false
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
it 'is false when save_shipping_address is nil' do
|
|
311
|
+
order.save_shipping_address = nil
|
|
312
|
+
order.save_shipping_address?.should eq false
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
describe '#to_param' do
|
|
317
|
+
it 'returns an obfuscated ID' do
|
|
318
|
+
(order.to_param.length >= 10).should eq true
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# # Attributes
|
|
4
|
+
describe Effective::StripeCharge do
|
|
5
|
+
let(:order) { FactoryGirl.create(:order_with_subscription) }
|
|
6
|
+
let(:charge) { Effective::StripeCharge.new(order) }
|
|
7
|
+
|
|
8
|
+
before { StripeMock.start }
|
|
9
|
+
after { StripeMock.stop }
|
|
10
|
+
|
|
11
|
+
it 'can be initialized with an order' do
|
|
12
|
+
charge = Effective::StripeCharge.new(order)
|
|
13
|
+
charge.order.should eq order
|
|
14
|
+
charge.effective_order_id.should eq order.to_param
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'can be initialized without an order' do
|
|
18
|
+
charge = Effective::StripeCharge.new(:token => 'tok_123', :effective_order_id => 3)
|
|
19
|
+
charge.token.should eq 'tok_123'
|
|
20
|
+
charge.effective_order_id.should eq 3
|
|
21
|
+
charge.order.nil?.should eq true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'performs validations' do
|
|
25
|
+
charge = Effective::StripeCharge.new()
|
|
26
|
+
charge.valid?.should eq false
|
|
27
|
+
charge.errors[:token].present?.should eq true
|
|
28
|
+
charge.errors[:effective_order_id].present?.should eq true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it '#order_items returns all OrderItems where the purchasable is not a Subscription' do
|
|
32
|
+
charge.order_items.all? { |oi| oi.purchasable_type != 'Effective::Subscription'}.should eq true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it '#subscriptions returns all Subscriptions (not order items)' do
|
|
36
|
+
charge.subscriptions.all? { |oi| oi.kind_of?(Effective::Subscription) }.should eq true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# # Attributes
|
|
4
|
+
describe Effective::Subscription do
|
|
5
|
+
let(:subscription) { FactoryGirl.create(:subscription) }
|
|
6
|
+
|
|
7
|
+
before { StripeMock.start }
|
|
8
|
+
after { StripeMock.stop }
|
|
9
|
+
|
|
10
|
+
describe '#stripe_plan' do
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#stripe_plan_id=' do
|
|
15
|
+
it 'assigns the stripe_plan_id' do
|
|
16
|
+
subscription.stripe_plan_id = 'Plan 9'
|
|
17
|
+
subscription.stripe_plan_id.should eq 'Plan 9'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'rejects an invalid stripe_plan' do
|
|
21
|
+
subscription.stripe_plan.present?.should eq true # From the Factory
|
|
22
|
+
|
|
23
|
+
subscription.stripe_plan_id = 'Plan 9' # Set it to an Invalid plan
|
|
24
|
+
subscription.stripe_plan_id.should eq 'Plan 9'
|
|
25
|
+
|
|
26
|
+
subscription.stripe_plan.should eq nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'sets the stripe plan' do
|
|
30
|
+
subscription = Effective::Subscription.new()
|
|
31
|
+
|
|
32
|
+
plan = ::Stripe::Plan.create()
|
|
33
|
+
subscription.stripe_plan_id = plan.id
|
|
34
|
+
subscription.stripe_plan.id.should eq plan.id
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#stripe_coupon_id=' do
|
|
39
|
+
it 'assigns the stripe_coupon_id' do
|
|
40
|
+
subscription.stripe_coupon_id = 'Plan 9'
|
|
41
|
+
subscription.stripe_coupon_id.should eq 'Plan 9'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'rejects an invalid stripe_coupon' do
|
|
45
|
+
subscription.stripe_coupon.present?.should eq true # From the Factory
|
|
46
|
+
|
|
47
|
+
subscription.stripe_coupon_id = 'Plan 9' # Set it to an Invalid plan
|
|
48
|
+
subscription.stripe_coupon_id.should eq 'Plan 9'
|
|
49
|
+
|
|
50
|
+
subscription.stripe_coupon.should eq nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'sets the stripe coupon' do
|
|
54
|
+
subscription = Effective::Subscription.new()
|
|
55
|
+
|
|
56
|
+
coupon = ::Stripe::Coupon.create()
|
|
57
|
+
subscription.stripe_coupon_id = coupon.id
|
|
58
|
+
subscription.stripe_coupon.id.should eq coupon.id
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe '#assign_price_and_title' do
|
|
63
|
+
it 'assigns the price and title as per the stripe plan and coupon' do
|
|
64
|
+
subscription = Effective::Subscription.new()
|
|
65
|
+
|
|
66
|
+
plan = ::Stripe::Plan.create()
|
|
67
|
+
|
|
68
|
+
subscription.stripe_plan_id = plan.id
|
|
69
|
+
|
|
70
|
+
subscription.price.should eq plan.amount
|
|
71
|
+
subscription.title.include?(plan.name).should eq true
|
|
72
|
+
subscription.title.include?(plan.interval).should eq true
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'assigns the price and title as per the stripe plan and coupon amount off' do
|
|
76
|
+
subscription = Effective::Subscription.new()
|
|
77
|
+
|
|
78
|
+
plan = ::Stripe::Plan.create()
|
|
79
|
+
coupon = ::Stripe::Coupon.create(:percent_off => 25)
|
|
80
|
+
|
|
81
|
+
subscription.stripe_plan_id = plan.id
|
|
82
|
+
subscription.stripe_coupon_id = coupon.id
|
|
83
|
+
|
|
84
|
+
subscription.price.should eq (plan.amount * (coupon.percent_off.to_i / 100.0)).floor
|
|
85
|
+
subscription.title.include?('25% off').should eq true
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'assigns the price and title as per the stripe plan and coupon percent off' do
|
|
89
|
+
subscription = Effective::Subscription.new()
|
|
90
|
+
|
|
91
|
+
plan = ::Stripe::Plan.create()
|
|
92
|
+
coupon = ::Stripe::Coupon.create(:percent_off => nil, :amount_off => 100)
|
|
93
|
+
|
|
94
|
+
subscription.stripe_plan_id = plan.id
|
|
95
|
+
subscription.stripe_coupon_id = coupon.id
|
|
96
|
+
|
|
97
|
+
subscription.price.should eq plan.amount - coupon.amount_off
|
|
98
|
+
subscription.title.include?('$1.00 off').should eq true
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
end
|