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
data/.rspec CHANGED
@@ -1 +1,4 @@
1
1
  --color
2
+ -I app/controllers
3
+ -I app/helpers
4
+ -I app/models
data/Gemfile CHANGED
@@ -1,14 +1,18 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem 'configurator2', '>= 0.1.2'
3
+ gem 'activerecord', '>= 3.2.11'
4
+ gem 'configurator2', '>= 0.1.3'
4
5
  gem 'i18n'
6
+ gem 'maintain'
5
7
  gem 'money-rails'
6
8
 
7
- group :development, :test do
8
- gem 'jeweler', '1.8.4'
9
- gem 'pry', require: false
10
- end
11
-
12
9
  group :test do
10
+ gem 'active_shipping'
11
+ gem 'database_cleaner'
12
+ gem 'jeweler', '1.8.4'
13
+ gem 'pry'
14
+ gem 'simplecov'
15
+ gem 'sqlite3'
13
16
  gem 'rspec-rails'
17
+ gem 'vcr'
14
18
  end
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- actionpack (3.2.9)
5
- activemodel (= 3.2.9)
6
- activesupport (= 3.2.9)
4
+ actionpack (3.2.11)
5
+ activemodel (= 3.2.11)
6
+ activesupport (= 3.2.11)
7
7
  builder (~> 3.0.0)
8
8
  erubis (~> 2.7.0)
9
9
  journey (~> 1.0.4)
@@ -11,15 +11,31 @@ GEM
11
11
  rack-cache (~> 1.2)
12
12
  rack-test (~> 0.6.1)
13
13
  sprockets (~> 2.2.1)
14
- activemodel (3.2.9)
15
- activesupport (= 3.2.9)
14
+ active_shipping (0.9.14)
15
+ active_utils (>= 1.0.1)
16
+ activesupport (>= 2.3.5)
17
+ builder
18
+ i18n
19
+ json (>= 1.5.1)
20
+ active_utils (1.0.5)
21
+ activesupport (>= 2.3.11)
22
+ i18n
23
+ activemodel (3.2.11)
24
+ activesupport (= 3.2.11)
16
25
  builder (~> 3.0.0)
17
- activesupport (3.2.9)
26
+ activerecord (3.2.11)
27
+ activemodel (= 3.2.11)
28
+ activesupport (= 3.2.11)
29
+ arel (~> 3.0.2)
30
+ tzinfo (~> 0.3.29)
31
+ activesupport (3.2.11)
18
32
  i18n (~> 0.6)
19
33
  multi_json (~> 1.0)
34
+ arel (3.0.2)
20
35
  builder (3.0.4)
21
36
  coderay (1.0.8)
22
- configurator2 (0.1.2)
37
+ configurator2 (0.1.3)
38
+ database_cleaner (0.9.1)
23
39
  diff-lcs (1.1.3)
24
40
  erubis (2.7.0)
25
41
  git (1.2.5)
@@ -32,6 +48,7 @@ GEM
32
48
  rdoc
33
49
  journey (1.0.4)
34
50
  json (1.7.6)
51
+ maintain (0.2.23)
35
52
  method_source (0.8.1)
36
53
  money (5.1.0)
37
54
  i18n (~> 0.6.0)
@@ -40,20 +57,20 @@ GEM
40
57
  money (~> 5.1.0)
41
58
  railties (~> 3.0)
42
59
  multi_json (1.5.0)
43
- pry (0.9.10)
60
+ pry (0.9.11.4)
44
61
  coderay (~> 1.0.5)
45
62
  method_source (~> 0.8)
46
- slop (~> 3.3.1)
47
- rack (1.4.1)
63
+ slop (~> 3.4)
64
+ rack (1.4.4)
48
65
  rack-cache (1.2)
49
66
  rack (>= 0.4)
50
- rack-ssl (1.3.2)
67
+ rack-ssl (1.3.3)
51
68
  rack
52
69
  rack-test (0.6.2)
53
70
  rack (>= 1.0)
54
- railties (3.2.9)
55
- actionpack (= 3.2.9)
56
- activesupport (= 3.2.9)
71
+ railties (3.2.11)
72
+ actionpack (= 3.2.11)
73
+ activesupport (= 3.2.11)
57
74
  rack-ssl (~> 1.3.2)
58
75
  rake (>= 0.8.7)
59
76
  rdoc (~> 3.4)
@@ -64,30 +81,44 @@ GEM
64
81
  rspec-core (2.12.2)
65
82
  rspec-expectations (2.12.1)
66
83
  diff-lcs (~> 1.1.3)
67
- rspec-mocks (2.12.1)
68
- rspec-rails (2.12.0)
84
+ rspec-mocks (2.12.2)
85
+ rspec-rails (2.12.2)
69
86
  actionpack (>= 3.0)
70
87
  activesupport (>= 3.0)
71
88
  railties (>= 3.0)
72
89
  rspec-core (~> 2.12.0)
73
90
  rspec-expectations (~> 2.12.0)
74
91
  rspec-mocks (~> 2.12.0)
75
- slop (3.3.3)
92
+ simplecov (0.7.1)
93
+ multi_json (~> 1.0)
94
+ simplecov-html (~> 0.7.1)
95
+ simplecov-html (0.7.1)
96
+ slop (3.4.3)
76
97
  sprockets (2.2.2)
77
98
  hike (~> 1.2)
78
99
  multi_json (~> 1.0)
79
100
  rack (~> 1.0)
80
101
  tilt (~> 1.1, != 1.3.0)
81
- thor (0.16.0)
102
+ sqlite3 (1.3.7)
103
+ thor (0.17.0)
82
104
  tilt (1.3.3)
105
+ tzinfo (0.3.35)
106
+ vcr (2.4.0)
83
107
 
84
108
  PLATFORMS
85
109
  ruby
86
110
 
87
111
  DEPENDENCIES
88
- configurator2 (>= 0.1.2)
112
+ active_shipping
113
+ activerecord (>= 3.2.11)
114
+ configurator2 (>= 0.1.3)
115
+ database_cleaner
89
116
  i18n
90
117
  jeweler (= 1.8.4)
118
+ maintain
91
119
  money-rails
92
120
  pry
93
121
  rspec-rails
122
+ simplecov
123
+ sqlite3
124
+ vcr
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.0
@@ -1,62 +1,53 @@
1
1
  class Bodega::OrdersController < ApplicationController
2
- helper 'bodega/cart'
3
- include Bodega::PaymentMethod
2
+ before_filter :find_order, only: %w(show)
4
3
 
5
- before_filter :find_order, only: [:show, :update]
4
+ helper 'bodega/cart'
6
5
 
7
6
  def add
8
- if product = params[:product]
7
+ if product = params[:order_product]
9
8
  update_cart(product)
10
9
  end
11
- redirect_to root_path
10
+ redirect_to new_order_path
12
11
  end
13
12
 
14
13
  def complete
15
- if current_order.finalize!(payment_method)
16
- current_products.clear
17
- redirect_to order_path(current_order)
14
+ if current_order.finalize!(params)
15
+ session.delete(:bodega_order_id)
16
+ redirect_to order_path(current_order), notice: t('bodega.order_processed')
18
17
  else
19
- flash[:error] = "There was a problem processing this order. Your account has not been charged."
20
- redirect_to new_order_path
18
+ redirect_to new_order_path, error: t('bodega.order_failed')
21
19
  end
22
20
  end
23
21
 
24
22
  def create
25
- params[:products].each do |product|
26
- update_cart(product)
27
- end
28
- if params[:checkout]
29
- redirect_to payment_method.checkout_url(complete_url, root_url)
23
+ if current_order.update_attributes(params[:order])
24
+ if params[:checkout]
25
+ redirect_to current_order.payment_method.checkout_url(complete_order_url, new_order_url)
26
+ else
27
+ redirect_to new_order_path
28
+ end
30
29
  else
31
30
  render :new
32
31
  end
33
32
  end
34
33
 
35
34
  def remove
36
- current_products.delete params[:product_id]
35
+ current_order.remove_product params[:product_id]
37
36
  redirect_to :back
38
37
  end
39
38
 
39
+ def show
40
+ render :edit unless @order.complete?
41
+ end
42
+
40
43
  protected
41
44
  def find_order
42
45
  raise ActiveRecord::RecordNotFound unless @order = Bodega::Order.where(identifier: params[:order_id] || params[:id]).first
43
46
  end
44
47
 
45
48
  def update_cart(product_hash)
46
- product_id = "#{product_hash[:type]}.#{product_hash[:id]}"
47
- if product_hash[:remove]
48
- current_products.delete product_id
49
- else
50
- if current_product = current_products[product_id]
51
- current_quantity = current_product[:quantity].to_i
52
- else
53
- current_quantity = 0
54
- end
55
- new_quantity = product_hash[:quantity] ? product_hash[:quantity].to_i : current_quantity + 1
56
- if product = product_hash[:type].constantize.where(id: product_hash[:id], keep_stock: true).first
57
- new_quantity = [product.number_in_stock, new_quantity].min
58
- end
59
- current_products[product_id] = product_hash.merge(quantity: new_quantity)
49
+ if current_order.update_product(product_hash)
50
+ session[:bodega_order_id] = current_order.identifier
60
51
  end
61
52
  end
62
53
  end
@@ -6,6 +6,7 @@ module Bodega
6
6
  alias :method_missing :method_missing_with_bodega
7
7
  end
8
8
  end
9
+
9
10
  protected
10
11
  def method_missing_with_bodega(method_name, *args)
11
12
  if method_name.to_s =~ /.+_(url|path)$/ && main_app.respond_to?(method_name)
@@ -1,39 +1,23 @@
1
1
  module Bodega
2
2
  module CartHelper
3
- def button_to_cart(product, label = 'Add to Cart', options = {})
3
+ def button_to_cart(product, label = 'Add to Cart', options = {}, &block)
4
4
  unless options.key? :disabled
5
5
  options[:disabled] = !product.in_stock?
6
6
  end
7
- form_tag(bodega.add_path) do
8
- hidden_field_tag('product[type]', product.class) +
9
- hidden_field_tag('product[id]', product.id) +
10
- button_tag(label, options)
7
+ form_contents = hidden_field_tag('order_product[product_type]', product.class)
8
+ form_contents << hidden_field_tag('order_product[product_id]', product.id) +
9
+ if block_given?
10
+ form_contents << capture(&block)
11
11
  end
12
+ form_contents << button_tag(label, options)
13
+ form_tag(bodega.add_to_order_path) { form_contents }
12
14
  end
13
15
 
14
16
  protected
15
17
  def current_order
16
- @current_order ||= Bodega::Order.new.tap do |order|
17
- begin
18
- if Bodega.config.customer_method
19
- order.customer = send(Bodega.config.customer_method)
20
- end
21
- rescue NoMethodError
22
- raise "Please configure Bodega.config.customer_method to point to a valid method for accessing a customer record (default: current_user)"
23
- end
24
- order.order_products = current_products.map do |type, product|
25
- product = product.symbolize_keys
26
- OrderProduct.new do |order_product|
27
- order_product.product_type = product[:type]
28
- order_product.product_id = product[:id]
29
- order_product.quantity = product[:quantity]
30
- end
31
- end
18
+ @current_order ||= Bodega::Order.where(identifier: session[:bodega_order_id]).first || Bodega::Order.new.tap do |order|
19
+ order.customer = send(Bodega.config.customer_method) if Bodega.config.customer_method
32
20
  end
33
21
  end
34
-
35
- def current_products
36
- session[:bodega_products] ||= {}
37
- end
38
22
  end
39
23
  end
@@ -1,43 +1,145 @@
1
+ require 'maintain'
2
+
1
3
  module Bodega
2
4
  class Order < ActiveRecord::Base
3
- before_save :set_total
5
+ self.table_name = :bodega_orders
6
+
7
+ attr_accessible :order_products_attributes, :street_1, :street_2, :city, :state, :postal_code, :shipping_rate_code
8
+
4
9
  before_create :set_identifier
10
+ before_save :set_shipping_rates, if: :postal_code_changed?
11
+ before_save :calculate_shipping, if: :shipping_rate_code_changed?
12
+ before_save :set_total
5
13
 
6
14
  belongs_to :customer, polymorphic: true
15
+
7
16
  has_many :order_products, class_name: 'Bodega::OrderProduct', dependent: :destroy
8
- has_many :products, through: :order_products
17
+ accepts_nested_attributes_for :order_products
9
18
 
19
+ delegate :empty?, to: :order_products
20
+
21
+ maintain :status do
22
+ state :new, 1, default: true
23
+ state :complete, 2
24
+
25
+ on :enter, :complete, :mark_order_products_as_purchased
26
+ end
27
+
28
+ monetize :shipping_cents
10
29
  monetize :tax_cents
11
30
  monetize :total_cents
12
31
 
13
- def finalize!(payment_method)
32
+ serialize :shipping_rates
33
+
34
+ def finalize!(options)
14
35
  self.class.transaction do
36
+ self.status = :complete
15
37
  self.save!
16
38
  begin
17
- self.payment_id = payment_method.complete!
18
- self.save!
19
- rescue Exception => e
39
+ self.payment_id = payment_method.complete!(options)
40
+ self.save
41
+ rescue Exception
20
42
  raise ActiveRecord::Rollback
21
- raise e.inspect
22
43
  end
23
44
  end
24
45
  end
25
46
 
26
- def subtotal
27
- @subtotal ||= order_products.inject(Money.new(0)) {|sum, order_product| sum += order_product.subtotal }
47
+ def payment_method
48
+ return nil unless Bodega.config.payment_method
49
+ @payment_method ||= "Bodega::PaymentMethod::#{Bodega.config.payment_method.to_s.camelize}".constantize.new(self)
28
50
  end
29
51
 
30
- def set_total
31
- self.total = subtotal + tax
52
+ def products
53
+ order_products.map(&:product)
54
+ end
55
+
56
+ def ready?
57
+ shipping_method.present? || shipping_rates.any?
58
+ end
59
+
60
+ def remove_product(item)
61
+ unless item.is_a?(Bodega::OrderProduct)
62
+ item = order_product(item)
63
+ end
64
+ item.destroy
65
+ order_products.delete(item)
66
+ end
67
+
68
+ def shipping_method
69
+ case Bodega.config.shipping_method
70
+ when :ups
71
+ Bodega::ShippingMethod::UPS.new(self)
72
+ end
73
+ end
74
+
75
+ def shipping_rate_options
76
+ @shipping_rate_options ||= ActiveSupport::OrderedHash.new.tap do |rates|
77
+ shipping_rates.sort_by {|code, rate| rate[:price] }.each do |code, rate|
78
+ name = rate[:name]
79
+ price = Money.new(rate[:price])
80
+ rates["#{name}: #{price.format}"] = code
81
+ end
82
+ end
83
+ end
84
+
85
+ def subtotal
86
+ order_products.inject(Money.new(0)) {|sum, order_product| sum += order_product.subtotal }
32
87
  end
33
88
 
34
89
  def to_param
35
90
  identifier
36
91
  end
37
92
 
93
+ def update_product(item)
94
+ if order_product = order_product(item)
95
+ if item[:remove]
96
+ remove_product(order_product)
97
+ else
98
+ current_quantity = order_product.quantity
99
+ new_quantity = item[:quantity] ? item[:quantity].to_i : current_quantity + 1
100
+ order_product.update_attributes(quantity: new_quantity)
101
+ end
102
+ else
103
+ order_product = order_products.build({quantity: 1}.merge(item))
104
+ end
105
+ save unless empty?
106
+ end
107
+
38
108
  protected
109
+ def calculate_shipping
110
+ self.shipping = 0
111
+ if shipping_rate_code
112
+ self.shipping_rate_name = shipping_rates[shipping_rate_code][:name]
113
+ self.shipping = shipping_rates[shipping_rate_code][:price] / 100.0
114
+ end
115
+ end
116
+
117
+ def calculate_tax
118
+ self.tax = 0
119
+ end
120
+
121
+ def mark_order_products_as_purchased
122
+ order_products.each(&:update_stock)
123
+ end
124
+
125
+ def order_product(item)
126
+ if item.is_a?(Hash)
127
+ order_products.detect {|order_product| order_product.product_type == item[:product_type] && order_product.product_id == item[:product_id].to_i }
128
+ else
129
+ order_products.detect {|order_product| order_product.identifier == item }
130
+ end
131
+ end
132
+
39
133
  def set_identifier
40
134
  self.identifier = self.class.count.succ.to_s(36)
41
135
  end
136
+
137
+ def set_shipping_rates
138
+ self.shipping_rates = postal_code.present? ? shipping_method.rates : nil
139
+ end
140
+
141
+ def set_total
142
+ self.total = subtotal + tax + shipping
143
+ end
42
144
  end
43
145
  end
@@ -1,6 +1,9 @@
1
1
  module Bodega
2
2
  class OrderProduct < ActiveRecord::Base
3
- after_save :update_stock
3
+ self.table_name = :bodega_order_products
4
+
5
+ attr_accessible :quantity, :product, :product_id, :product_type
6
+
4
7
  before_save :calculate_total
5
8
 
6
9
  belongs_to :order, class_name: 'Bodega::Order'
@@ -11,7 +14,7 @@ module Bodega
11
14
  monetize :total_cents
12
15
 
13
16
  validates_numericality_of :quantity, allow_blank: true, minimum: 1
14
- validates_presence_of :quantity
17
+ validates_presence_of :product, :quantity
15
18
  validate :product_available?
16
19
 
17
20
  def identifier
@@ -19,7 +22,14 @@ module Bodega
19
22
  end
20
23
 
21
24
  def name
22
- product.respond_to?(:name) ? product.name : product.to_s
25
+ product.respond_to?(:name) ? product.name : "#{product_type.titleize} ##{product_id}"
26
+ end
27
+
28
+ def update_stock
29
+ if keep_stock?
30
+ product.number_in_stock = product.number_in_stock - quantity
31
+ product.save(validate: false)
32
+ end
23
33
  end
24
34
 
25
35
  def quantity_and_name
@@ -36,31 +46,15 @@ module Bodega
36
46
  end
37
47
 
38
48
  def product_available?
39
- unless product.number_in_stock >= quantity
40
- quantity_error = case quantity
41
- when 1
42
- "We're sorry, but the #{name} you requested is no longer in stock."
43
- else
44
- "We're sorry, but there are no longer #{quantity} #{name.pluralize} in stock."
45
- end
46
-
47
- quantity_message = case product.number_in_stock
48
- when 0
49
- "They are now sold out."
50
- when 1
51
- "There is now one in stock."
49
+ return true unless product.keep_stock?
50
+ if !product.in_stock?
51
+ errors.add(:quantity, I18n.t("bodega.sold_out"))
52
+ elsif product.number_in_stock < quantity
53
+ if product.number_in_stock == 1
54
+ errors.add(:quantity, I18n.t("bodega.one_in_stock"))
52
55
  else
53
- "There are now #{quantity} in stock."
56
+ errors.add(:quantity, I18n.t("bodega.x_in_stock").gsub(' x ', " #{product.number_in_stock} "))
54
57
  end
55
-
56
- errors.add(:quantity, "#{quantity_error} #{quantity_message}.")
57
- end
58
- end
59
-
60
- def update_stock
61
- if keep_stock?
62
- product.number_in_stock = product.number_in_stock - quantity
63
- product.save(validate: false)
64
58
  end
65
59
  end
66
60
  end
@@ -8,31 +8,28 @@ module Bodega
8
8
  monetize :price_cents
9
9
 
10
10
  scope :for_sale, lambda {
11
- where %[
12
- for_sale IS TRUE OR
13
- (
14
- (for_sale_at >= :today OR for_sale_at IS NULL) AND
15
- (not_for_sale_at <= :today OR not_for_sale_at IS NULL) AND
16
- (for_sale_at IS NULL AND not_for_sale_at IS NULL) IS NOT TRUE
17
- )
18
- ], today: Date.today
11
+ today = Date.today
12
+ where(for_sale: true).
13
+ where(arel_table[:for_sale_at].lteq(today).or(arel_table[:for_sale_at].eq(nil))).
14
+ where(arel_table[:not_for_sale_at].gteq(today).or(arel_table[:not_for_sale_at].eq(nil)))
19
15
  }
20
16
 
21
- # TODO: Get this to use a regular JOIN
22
17
  scope :popular, joins(%(LEFT JOIN "bodega_order_products" ON "bodega_order_products"."product_id" = "#{table_name}"."id" AND "bodega_order_products"."product_type" = '#{name}')).order('SUM(bodega_order_products.quantity) DESC').group("#{table_name}.id")
18
+
19
+ validates_numericality_of :number_in_stock, :if => :keep_stock?
23
20
  end
24
21
  end
25
22
 
26
23
  def in_stock?
27
- if keep_stock?
24
+ if keep_stock? && number_in_stock
28
25
  number_in_stock > 0
29
26
  else
30
27
  true
31
28
  end
32
29
  end
33
30
 
34
- def max_for_sale
35
- keep_stock? ? number_in_stock : 1000
31
+ def max_quantity
32
+ keep_stock? ? number_in_stock : Bodega.config.max_quantity
36
33
  end
37
34
  end
38
35
  end
@@ -0,0 +1,26 @@
1
+ <%= form_for(current_order, method: :post, url: new_order_path) do |form| %>
2
+ <table id="bodega-cart">
3
+ <thead>
4
+ <tr><th class="product-name" colspan="2"><%= t 'bodega.product' %></th><th class="product-price"><%= t 'bodega.price' %></th><th class="product-total" colspan="2"><%= t 'bodega.total' %></th></tr>
5
+ </thead>
6
+ <tbody>
7
+ <%= form.fields_for(:order_products) do |order_product_form| -%>
8
+ <%= render partial: 'cart_row', locals: {form: order_product_form, order_product: order_product_form.object} %>
9
+ <% end =%>
10
+ </tbody>
11
+ <tfoot>
12
+ <%= render partial: 'shipping_row', locals: {form: form} if current_order.shipping_method %>
13
+ <% if current_order.ready? -%>
14
+ <tr>
15
+ <td class="product-total-label" colspan="3"><%= t 'bodega.total' %></td>
16
+ <td class="product-total"><%= humanized_money_with_symbol current_order.total %></td>
17
+ <td></td>
18
+ </tr>
19
+ <% end -%>
20
+ </tfoot>
21
+ </table>
22
+ <%= button_tag t('bodega.update_cart'), id: 'bodega-update', name: :update, value: 1 %>
23
+ <% if current_order.ready? -%>
24
+ <%= button_tag t('bodega.checkout'), id: 'bodega-checkout', name: :checkout, value: 1 %>
25
+ <% end -%>
26
+ <% end =%>
@@ -0,0 +1,24 @@
1
+ <tr id="<%= order_product.identifier.parameterize %>">
2
+ <td class="product-quantity">
3
+ <%= form.number_field :quantity, class: 'quantity', max: order_product.product.max_quantity, min: 1 %>
4
+ <% if order_product.errors[:quantity].any? -%>
5
+ <span class="bodega-errors">
6
+ <%= order_product.errors[:quantity].join('<br />').html_safe %>
7
+ </span>
8
+ <% end -%>
9
+ </td>
10
+ <td class="product-name">
11
+ <%= order_product.name %>
12
+ <%= form.hidden_field :product_type %>
13
+ <%= form.hidden_field :product_id %>
14
+ </td>
15
+ <td class="product-price">
16
+ <%= humanized_money_with_symbol order_product.price %>
17
+ </td>
18
+ <td class="product-subtotal">
19
+ <%= humanized_money_with_symbol order_product.subtotal %>
20
+ </td>
21
+ <td class="product-remove">
22
+ <%= link_to t('bodega.remove'), bodega.remove_from_order_path(product_id: order_product.identifier) %>
23
+ </td>
24
+ </tr>
@@ -0,0 +1,13 @@
1
+ <tr>
2
+ <td class="shipping-postal-code">
3
+ <%= form.text_field :postal_code, placeholder: t('bodega.postal_code') %>
4
+ </td>
5
+ <td class="shipping-rates" colspan="2">
6
+ <% if current_order.postal_code.present? -%>
7
+ <%= form.select :shipping_rate_code, current_order.shipping_rate_options %>
8
+ <% end -%>
9
+ </td>
10
+ <td class="shipping-total" colspan="2">
11
+ <%= humanized_money_with_symbol current_order.shipping %>
12
+ </td>
13
+ </tr>
@@ -0,0 +1 @@
1
+ <%= render :partial => 'cart' %>