bodega 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.rspec +3 -0
  2. data/Gemfile +10 -6
  3. data/Gemfile.lock +50 -19
  4. data/VERSION +1 -1
  5. data/app/controllers/bodega/orders_controller.rb +21 -30
  6. data/app/helpers/bodega/application_helper.rb +1 -0
  7. data/app/helpers/bodega/cart_helper.rb +9 -25
  8. data/app/models/bodega/order.rb +113 -11
  9. data/app/models/bodega/order_product.rb +20 -26
  10. data/app/models/bodega/product.rb +9 -12
  11. data/app/views/bodega/orders/_cart.html.erb +26 -0
  12. data/app/views/bodega/orders/_cart_row.html.erb +24 -0
  13. data/app/views/bodega/orders/_shipping_row.html.erb +13 -0
  14. data/app/views/bodega/orders/edit.html.erb +1 -0
  15. data/app/views/bodega/orders/new.html.erb +2 -38
  16. data/bodega.gemspec +30 -12
  17. data/config/locales/en.yml +13 -0
  18. data/config/routes.rb +9 -9
  19. data/db/migrate/20121111170337_create_bodega_orders.rb +12 -0
  20. data/lib/bodega/engine.rb +0 -1
  21. data/lib/bodega/optional.rb +12 -0
  22. data/lib/bodega/payment_method/base.rb +5 -13
  23. data/lib/bodega/payment_method/paypal.rb +12 -4
  24. data/lib/bodega/payment_method.rb +0 -4
  25. data/lib/bodega/shipping_method/base.rb +71 -0
  26. data/lib/bodega/shipping_method/ups.rb +18 -0
  27. data/lib/bodega/shipping_method.rb +5 -0
  28. data/lib/bodega.rb +19 -1
  29. data/spec/lib/bodega/payment_method/base_spec.rb +12 -0
  30. data/spec/lib/bodega/shipping_method/base_spec.rb +12 -0
  31. data/spec/lib/bodega_spec.rb +18 -0
  32. data/spec/models/order_product_spec.rb +72 -0
  33. data/spec/models/order_spec.rb +93 -0
  34. data/spec/models/product_spec.rb +78 -0
  35. data/spec/spec_helper.rb +25 -0
  36. data/spec/support/active_record.rb +43 -0
  37. data/spec/support/rails.rb +7 -0
  38. data/spec/support/vcr.rb +0 -0
  39. metadata +36 -18
@@ -1,41 +1,5 @@
1
- <% if current_products.empty? -%>
1
+ <% if current_order.empty? -%>
2
2
  <h3><%= t 'bodega.empty_cart' %></h3>
3
3
  <% else -%>
4
- <%= form_for(current_order, url: root_path) do |form| %>
5
- <table id="bodega-cart">
6
- <thead>
7
- <tr><th class="product-name" colspan="2"><%= t 'bodega.product' %></th><th class="price"><%= t 'bodega.price' %></th><th class="total" colspan="2"><%= t 'bodega.total' %></th></tr>
8
- </thead>
9
- <tbody>
10
- <% current_order.order_products.each do |order_product| -%>
11
- <tr>
12
- <td class="quantity-field">
13
- <%= number_field_tag 'products[][quantity]', order_product.quantity, class: 'quantity', max: order_product.product.max_for_sale, min: 1 %>
14
- </td>
15
- <td class="product-name">
16
- <%= order_product.name %>
17
- <%= hidden_field_tag 'products[][type]', order_product.product_type %>
18
- <%= hidden_field_tag 'products[][id]', order_product.product_id %>
19
- </td>
20
- <td class="price">
21
- <%= humanized_money_with_symbol order_product.price %>
22
- </td>
23
- <td class="subtotal">
24
- <%= humanized_money_with_symbol order_product.subtotal %>
25
- </td>
26
- <td class="remove">
27
- <%= link_to t('bodega.remove'), bodega.remove_path(product_id: order_product.identifier) %>
28
- </td>
29
- </tr>
30
- <% end -%>
31
- <tr>
32
- <td colspan="3"></td>
33
- <td><%= humanized_money_with_symbol current_order.subtotal %></td>
34
- <td></td>
35
- </tr>
36
- </tbody>
37
- </table>
38
- <%= button_tag t('bodega.update_cart'), id: 'bodega-update', name: :update, value: 1 %>
39
- <%= button_tag t('bodega.checkout'), id: 'bodega-checkout', name: :checkout, value: 1 %>
40
- <% end =%>
4
+ <%= render :partial => 'cart' %>
41
5
  <% end -%>
data/bodega.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "bodega"
8
- s.version = "0.3.0"
8
+ s.version = "0.3.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Flip Sasser"]
12
- s.date = "2013-01-07"
12
+ s.date = "2013-02-04"
13
13
  s.description = "Bodega adds checkout logic to any model in your app!"
14
14
  s.email = "flip@x451.com"
15
15
  s.extra_rdoc_files = [
@@ -33,6 +33,10 @@ Gem::Specification.new do |s|
33
33
  "app/models/bodega/order.rb",
34
34
  "app/models/bodega/order_product.rb",
35
35
  "app/models/bodega/product.rb",
36
+ "app/views/bodega/orders/_cart.html.erb",
37
+ "app/views/bodega/orders/_cart_row.html.erb",
38
+ "app/views/bodega/orders/_shipping_row.html.erb",
39
+ "app/views/bodega/orders/edit.html.erb",
36
40
  "app/views/bodega/orders/new.html.erb",
37
41
  "app/views/bodega/orders/show.html.erb",
38
42
  "bodega.gemspec",
@@ -42,9 +46,13 @@ Gem::Specification.new do |s|
42
46
  "db/migrate/20121111170420_create_bodega_order_products.rb",
43
47
  "lib/bodega.rb",
44
48
  "lib/bodega/engine.rb",
49
+ "lib/bodega/optional.rb",
45
50
  "lib/bodega/payment_method.rb",
46
51
  "lib/bodega/payment_method/base.rb",
47
52
  "lib/bodega/payment_method/paypal.rb",
53
+ "lib/bodega/shipping_method.rb",
54
+ "lib/bodega/shipping_method/base.rb",
55
+ "lib/bodega/shipping_method/ups.rb",
48
56
  "lib/bodega/version.rb",
49
57
  "lib/generators/bodega/install/install_generator.rb",
50
58
  "lib/generators/bodega/product/USAGE",
@@ -55,7 +63,17 @@ Gem::Specification.new do |s|
55
63
  "lib/generators/bodega/productize/productize_generator.rb",
56
64
  "lib/generators/bodega/productize/templates/migration.rb",
57
65
  "lib/tasks/bodega_tasks.rake",
58
- "script/rails"
66
+ "script/rails",
67
+ "spec/lib/bodega/payment_method/base_spec.rb",
68
+ "spec/lib/bodega/shipping_method/base_spec.rb",
69
+ "spec/lib/bodega_spec.rb",
70
+ "spec/models/order_product_spec.rb",
71
+ "spec/models/order_spec.rb",
72
+ "spec/models/product_spec.rb",
73
+ "spec/spec_helper.rb",
74
+ "spec/support/active_record.rb",
75
+ "spec/support/rails.rb",
76
+ "spec/support/vcr.rb"
59
77
  ]
60
78
  s.homepage = "http://github.com/flipsasser/bodega"
61
79
  s.licenses = ["MIT"]
@@ -67,24 +85,24 @@ Gem::Specification.new do |s|
67
85
  s.specification_version = 3
68
86
 
69
87
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
70
- s.add_runtime_dependency(%q<configurator2>, [">= 0.1.2"])
88
+ s.add_runtime_dependency(%q<activerecord>, [">= 3.2.11"])
89
+ s.add_runtime_dependency(%q<configurator2>, [">= 0.1.3"])
71
90
  s.add_runtime_dependency(%q<i18n>, [">= 0"])
91
+ s.add_runtime_dependency(%q<maintain>, [">= 0"])
72
92
  s.add_runtime_dependency(%q<money-rails>, [">= 0"])
73
- s.add_development_dependency(%q<jeweler>, ["= 1.8.4"])
74
- s.add_development_dependency(%q<pry>, [">= 0"])
75
93
  else
76
- s.add_dependency(%q<configurator2>, [">= 0.1.2"])
94
+ s.add_dependency(%q<activerecord>, [">= 3.2.11"])
95
+ s.add_dependency(%q<configurator2>, [">= 0.1.3"])
77
96
  s.add_dependency(%q<i18n>, [">= 0"])
97
+ s.add_dependency(%q<maintain>, [">= 0"])
78
98
  s.add_dependency(%q<money-rails>, [">= 0"])
79
- s.add_dependency(%q<jeweler>, ["= 1.8.4"])
80
- s.add_dependency(%q<pry>, [">= 0"])
81
99
  end
82
100
  else
83
- s.add_dependency(%q<configurator2>, [">= 0.1.2"])
101
+ s.add_dependency(%q<activerecord>, [">= 3.2.11"])
102
+ s.add_dependency(%q<configurator2>, [">= 0.1.3"])
84
103
  s.add_dependency(%q<i18n>, [">= 0"])
104
+ s.add_dependency(%q<maintain>, [">= 0"])
85
105
  s.add_dependency(%q<money-rails>, [">= 0"])
86
- s.add_dependency(%q<jeweler>, ["= 1.8.4"])
87
- s.add_dependency(%q<pry>, [">= 0"])
88
106
  end
89
107
  end
90
108
 
@@ -1,3 +1,16 @@
1
1
  en:
2
2
  bodega:
3
+ total: "Total"
4
+ grand_total: "Grand total:"
5
+ check_out: "Check Out"
6
+ update_cart: "Update Cart"
3
7
  empty_cart: "Your cart is currently empty."
8
+ sold_out: "Sorry, this product is sold out."
9
+ one_in_stock: "There is only one in stock!"
10
+ x_in_stock: "There are only x in stock!"
11
+ street_1: "123 Main Street"
12
+ street_2: "Apartment #1B"
13
+ city: "City"
14
+ postal_code: "Zip"
15
+ order_processed: "Your order has been processed! Thank you!"
16
+ order_failed: "There was a problem processing this order. Your account has not been charged."
data/config/routes.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  Bodega::Engine.routes.draw do
2
2
  # Building orders
3
- get '', as: :root, to: 'orders#new'
4
- post '', to: 'orders#create'
3
+ get '', as: :new_order, to: 'orders#new'
4
+ resource :order, only: :create, path: '' do
5
+ # Add products to an order
6
+ post :add, as: :add_to, to: 'orders#add'
7
+ get 'remove/:product_id', as: :remove_from, constraints: {product_id: /.+\.\d+/}, to: 'orders#remove'
5
8
 
6
- # Add products to an order
7
- post :add, to: 'orders#add'
8
- get 'remove/:product_id', as: :remove, constraints: {product_id: /.+\.\d+/}, to: 'orders#remove'
9
-
10
- # Processing orders
11
- get :complete, to: 'orders#complete'
12
- post :complete, to: 'orders#complete'
9
+ # Processing orders
10
+ get :complete, to: 'orders#complete'
11
+ post :complete, to: 'orders#complete'
12
+ end
13
13
 
14
14
  # Existing orders
15
15
  get ':id', as: :order, to: 'orders#show'
@@ -4,8 +4,20 @@ class CreateBodegaOrders < ActiveRecord::Migration
4
4
  def change
5
5
  create_table :bodega_orders do |t|
6
6
  t.belongs_to :customer, polymorphic: true
7
+ t.integer :status
7
8
  t.string :identifier, limit: 20
8
9
  t.string :payment_id
10
+ t.string :shipping_rate_code
11
+ t.string :shipping_rate_name, limit: 50
12
+ t.text :shipping_rates
13
+ t.string :tracking_number
14
+ t.string :street_1, limit: 60
15
+ t.string :street_2, limit: 60
16
+ t.string :city, limit: 60
17
+ t.string :state, limit: 3
18
+ t.string :postal_code, limit: 11
19
+ t.string :country, limit: 3
20
+ t.money :shipping
9
21
  t.money :tax
10
22
  t.money :total
11
23
  t.timestamps
data/lib/bodega/engine.rb CHANGED
@@ -4,7 +4,6 @@ module Bodega
4
4
 
5
5
  initializer "bodega.hookses" do
6
6
  ActiveSupport.on_load :action_controller do
7
- #helper 'bodega/application'
8
7
  helper 'bodega/cart'
9
8
  include Bodega::CartHelper
10
9
  end
@@ -0,0 +1,12 @@
1
+ module Bodega
2
+ module Optional
3
+ def options(*new_options)
4
+ option_namespace = self.name.split('::').pop.underscore
5
+ Bodega.class_eval do
6
+ option option_namespace do
7
+ options(*new_options.flatten)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,16 +1,9 @@
1
+ require 'bodega/optional'
2
+
1
3
  module Bodega
2
4
  module PaymentMethod
3
5
  class Base
4
- class << self
5
- def options(*new_options)
6
- option_namespace = self.name.split('::').pop.underscore
7
- Bodega.class_eval do
8
- option option_namespace do
9
- options(*new_options.flatten)
10
- end
11
- end
12
- end
13
- end
6
+ extend Bodega::Optional
14
7
 
15
8
  attr_accessor :options, :order
16
9
 
@@ -18,13 +11,12 @@ module Bodega
18
11
  raise "Implement #{self.class.name}#checkout_url"
19
12
  end
20
13
 
21
- def complete!
14
+ def complete!(options = {})
22
15
  raise "Implement #{self.class.name}#complete!"
23
16
  end
24
17
 
25
- def initialize(order, options)
18
+ def initialize(order)
26
19
  self.order = order
27
- self.options = options
28
20
  end
29
21
  end
30
22
  end
@@ -10,13 +10,12 @@ module Bodega
10
10
  response.redirect_uri
11
11
  end
12
12
 
13
- def complete!
13
+ def complete!(options = {})
14
14
  response = client.checkout!(
15
15
  options[:token],
16
16
  options[:PayerID],
17
17
  request
18
18
  )
19
- require 'pry'; binding.pry
20
19
  response.payment_info.last.transaction_id
21
20
  end
22
21
 
@@ -32,8 +31,17 @@ module Bodega
32
31
 
33
32
  def request
34
33
  @request ||= ::Paypal::Payment::Request.new(
35
- amount: order.subtotal.to_f,
36
- description: order.order_products.map(&:quantity_and_name).to_sentence
34
+ amount: order.total.to_f,
35
+ description: order.order_products.map(&:quantity_and_name).to_sentence,
36
+ items: order.order_products.map {|order_product|
37
+ {
38
+ name: order_product.name,
39
+ amount: order_product.price.to_f,
40
+ quantity: order_product.quantity
41
+ }
42
+ },
43
+ shipping_amount: order.shipping.to_f,
44
+ tax_amount: order.tax.to_f
37
45
  )
38
46
  end
39
47
  end
@@ -2,9 +2,5 @@ module Bodega
2
2
  module PaymentMethod
3
3
  autoload :Paypal, 'bodega/payment_method/paypal'
4
4
  autoload :Plinq, 'bodega/payment_method/plinq'
5
-
6
- def payment_method
7
- @payment_method ||= "Bodega::PaymentMethod::#{Bodega.config.payment_method.to_s.classify}".constantize.new(current_order, params)
8
- end
9
5
  end
10
6
  end
@@ -0,0 +1,71 @@
1
+ require 'bodega/optional'
2
+
3
+ module Bodega
4
+ module ShippingMethod
5
+ class Base
6
+ extend Bodega::Optional
7
+ include ActiveMerchant::Shipping if defined?(ActiveMerchant)
8
+
9
+ attr_accessor :order
10
+
11
+ def initialize(order)
12
+ self.order = order
13
+ end
14
+
15
+ def rates
16
+ return {} unless packages.any?
17
+ @rates ||= {}.tap do |rates|
18
+ response = client.find_rates(origin, destination, packages)
19
+ response.rates.sort_by(&:price).each do |rate|
20
+ rates[rate.service_code] = {
21
+ name: rate.service_name,
22
+ price: rate.price
23
+ }
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+ def client
30
+ raise "Implement #{self.class}#client to return an instance of an ActiveMerchant::Shipping method"
31
+ end
32
+
33
+ def destination
34
+ @destination ||= location_for(order)
35
+ end
36
+
37
+ def location_for(location_object)
38
+ Location.new(
39
+ city: location_object.city,
40
+ state: location_object.state,
41
+ zip: location_object.postal_code,
42
+ country: location_object.country
43
+ )
44
+ end
45
+
46
+ def origin
47
+ @origin ||= location_for(Bodega.config.shipping.origin)
48
+ end
49
+
50
+ def packages
51
+ @packages ||= [].tap do |packages|
52
+ order.products.each do |product|
53
+ packages.push(package_for(product)) if shippable?(product)
54
+ end
55
+ end
56
+ end
57
+
58
+ def package_for(product)
59
+ Package.new(
60
+ product.weight,
61
+ product.dimensions,
62
+ units: Bodega.config.shipping.units
63
+ )
64
+ end
65
+
66
+ def shippable?(product)
67
+ product.respond_to?(:weight) && product.respond_to?(:dimensions)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,18 @@
1
+ require 'bodega/shipping_method/base'
2
+
3
+ module Bodega
4
+ module ShippingMethod
5
+ class UPS < Base
6
+ options :login, :password, :api_key
7
+
8
+ protected
9
+ def client
10
+ @client ||= ActiveMerchant::Shipping::UPS.new(
11
+ login: Bodega.config.ups.login,
12
+ password: Bodega.config.ups.password,
13
+ key: Bodega.config.ups.api_key
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ module Bodega
2
+ module ShippingMethod
3
+ autoload :UPS, 'bodega/shipping_method/ups'
4
+ end
5
+ end
data/lib/bodega.rb CHANGED
@@ -1,13 +1,16 @@
1
- require 'bodega/engine'
1
+ require 'bodega/engine' if defined?(Rails)
2
2
  require 'configurator'
3
3
  require 'i18n'
4
4
  require 'money-rails'
5
5
 
6
6
  module Bodega
7
7
  autoload :PaymentMethod, 'bodega/payment_method'
8
+ autoload :ShippingMethod, 'bodega/shipping_method'
8
9
 
9
10
  extend Configurator
10
11
  option :customer_method, :current_user
12
+ option :max_quantity, 1000
13
+
11
14
  # Auto-detect payment method. If a user has the Paypal gem installed,
12
15
  # it'll use that. If a user has the Plinq gem installed, it'll use that.
13
16
  # Otherwise, it'll be all, "HEY I NEED A PAYMENT METHOD" when checkout
@@ -16,6 +19,21 @@ module Bodega
16
19
  defined?(::Plinq) ? :plinq : defined?(::Paypal) ? :paypal : raise("No payment method detected. Please set one using `Bodega.config.payment_method=`")
17
20
  }
18
21
 
22
+ # Defaults to no shipping. Change to :fedex, :ups, or :usps and add
23
+ # `gem "active_shipping"` to gain access to various shipping calculations
24
+ # in the checkout process.
25
+ option :shipping_method, nil
26
+ option :shipping do
27
+ origin do
28
+ city nil
29
+ state nil
30
+ postal_code nil
31
+ country nil
32
+ end
33
+ states []
34
+ units :metric
35
+ end
36
+
19
37
  # Auto-detect test mode. Defaults to true if running in development or test
20
38
  # mode.
21
39
  option :test_mode, lambda { Rails.env.development? || Rails.env.test? }
@@ -0,0 +1,12 @@
1
+ require 'bodega'
2
+ require 'bodega/payment_method/base'
3
+ require 'active_support/core_ext/string/inflections'
4
+
5
+ describe Bodega::PaymentMethod::Base do
6
+ describe ".options" do
7
+ it "defines options on the Bodega configuration instance" do
8
+ described_class.options(:a, :b, :z)
9
+ Bodega.config.base.should_not be_nil
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'bodega'
2
+ require 'bodega/shipping_method/base'
3
+ require 'active_support/core_ext/string/inflections'
4
+
5
+ describe Bodega::PaymentMethod::Base do
6
+ describe ".options" do
7
+ it "defines options on the Bodega configuration instance" do
8
+ described_class.options(:a, :b, :z)
9
+ Bodega.config.base.should_not be_nil
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bodega do
4
+ describe ".config" do
5
+ describe "#payment_method" do
6
+ it "auto-detects Paypal" do
7
+ class Paypal; end
8
+ Bodega.config.payment_method.should == :paypal
9
+ end
10
+ end
11
+
12
+ describe "test_mode" do
13
+ it "auto-detects test mode" do
14
+ Bodega.config.test_mode.should be_true
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'bodega/order_product'
3
+
4
+ describe Bodega::OrderProduct do
5
+ let(:product) { TestProduct.create!(product_attrs) }
6
+ let(:product_attrs) { {price: 49.95} }
7
+ let(:order_product) { described_class.new(product: product, quantity: 1) }
8
+
9
+ describe "#identifier" do
10
+ it "returns a friendly identifier" do
11
+ order_product.identifier.should == "TestProduct.1"
12
+ end
13
+ end
14
+
15
+ describe "#name" do
16
+ it "defaults to a human-readable name" do
17
+ order_product.name.should == "Test Product #1"
18
+ end
19
+
20
+ it "delegates to the product" do
21
+ def product.name
22
+ "Ohai"
23
+ end
24
+ order_product.name.should == "Ohai"
25
+ end
26
+ end
27
+
28
+ describe "#quantity_and_name" do
29
+ it "returns the quantity and the name" do
30
+ order_product.quantity_and_name.should == "1 x Test Product #1"
31
+ end
32
+ end
33
+
34
+ describe "#subtotal" do
35
+ it "returns the subtotal for the product quantity" do
36
+ product.stub(:price) { 25.0 }
37
+ order_product.quantity = 2
38
+ order_product.subtotal.should == 50.0
39
+ end
40
+ end
41
+
42
+ describe "for stock-kept products" do
43
+ let(:product_attrs) { {price: 49.95, keep_stock: true, number_in_stock: 1} }
44
+
45
+ it "can't be saved if the product is out-of-stock" do
46
+ product.stub(:in_stock?) { false }
47
+ product.save!
48
+ order_product.save
49
+ order_product.errors[:quantity].first.should == "Sorry, this product is sold out."
50
+ end
51
+
52
+ it "notifies me if my quantity is higher than the number left" do
53
+ order_product.quantity = 2
54
+ order_product.save
55
+ order_product.errors[:quantity].first.should == "There is only one in stock!"
56
+ end
57
+
58
+ it "can't be saved if the quantity is too high" do
59
+ product.stub(:number_in_stock) { 2 }
60
+ order_product.quantity = 3
61
+ order_product.save
62
+ order_product.errors[:quantity].first.should == "There are only 2 in stock!"
63
+ end
64
+
65
+ it "reduces Product#number_in_stock when #update_stock is called" do
66
+ order_product.stub(:order) { OpenStruct.new }
67
+ order_product.save!
68
+ order_product.update_stock
69
+ product.reload.number_in_stock.should == 0
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+ require 'bodega/order'
3
+ require 'bodega/order_product'
4
+ require 'bodega/product'
5
+
6
+ describe Bodega::Order do
7
+ before do
8
+ TestProduct.send :include, Bodega::Product
9
+ end
10
+
11
+ let!(:product_1) { TestProduct.create!(price: 30) }
12
+ let!(:product_2) { TestProduct.create!(price: 25) }
13
+ let(:order) { Bodega::Order.new }
14
+
15
+ describe "#payment_method" do
16
+ require 'bodega'
17
+ require 'bodega/payment_method'
18
+ class Paypal; end
19
+
20
+ it "returns an instance of Bodega::PaymentMethod::Base" do
21
+ order.payment_method.should be_instance_of(Bodega::PaymentMethod::Paypal)
22
+ end
23
+
24
+ it "returns an instance of Bodega::PaymentMethod::Base with a reference to the order" do
25
+ order.payment_method.order.should == order
26
+ end
27
+ end
28
+
29
+ describe "#shipping_method" do
30
+ require 'bodega'
31
+ require 'bodega/shipping_method'
32
+
33
+ before do
34
+ Bodega.config { shipping_method :ups }
35
+ module ActiveMerchant; module Shipping; class UPS; end; end; end
36
+ end
37
+
38
+ it "returns an instance of Bodega::ShippingMethod::Base" do
39
+ order.shipping_method.should be_instance_of(Bodega::ShippingMethod::UPS)
40
+ end
41
+ end
42
+
43
+ describe "#subtotal" do
44
+ let(:cart) do
45
+ {
46
+ "TestProduct.1" => {
47
+ product_type: "TestProduct",
48
+ product_id: "1",
49
+ quantity: "1"
50
+ },
51
+ "TestProduct.2" => {
52
+ product_type: "TestProduct",
53
+ product_id: "2",
54
+ quantity: "2"
55
+ }
56
+ }
57
+ end
58
+
59
+ it "adds up the #order_products subtotals" do
60
+ cart.each do |identifier, item|
61
+ order.update_product(item)
62
+ end
63
+ order.save!
64
+ order.subtotal.should == 80
65
+ end
66
+ end
67
+
68
+ describe "cart management" do
69
+ before { order.save! }
70
+
71
+ it "defaults to quantity 1 when none given" do
72
+ order.update_product(product_type: "TestProduct", product_id: 1)
73
+ order.send(:order_product, "TestProduct.1").quantity.should == 1
74
+ end
75
+
76
+ it "accepts new quantities" do
77
+ order.update_product(product_type: "TestProduct", product_id: 1, quantity: 10)
78
+ order.send(:order_product, "TestProduct.1").quantity.should == 10
79
+ end
80
+
81
+ it "updates old quantities when no new quantity is given" do
82
+ order.update_product(product_type: "TestProduct", product_id: 1, quantity: 10)
83
+ order.update_product(product_type: "TestProduct", product_id: 1)
84
+ order.send(:order_product, "TestProduct.1").quantity.should == 11
85
+ end
86
+
87
+ it "removes products when told to" do
88
+ order.update_product(product_type: "TestProduct", product_id: 1, quantity: 10)
89
+ order.update_product(product_type: "TestProduct", product_id: 1, quantity: 30, remove: "1")
90
+ order.send(:order_product, "TestProduct.1").should be_nil
91
+ end
92
+ end
93
+ end