bodega 0.3.0 → 0.4.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.
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' %>