bodega 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +3 -0
- data/Gemfile +10 -6
- data/Gemfile.lock +50 -19
- data/VERSION +1 -1
- data/app/controllers/bodega/orders_controller.rb +21 -30
- data/app/helpers/bodega/application_helper.rb +1 -0
- data/app/helpers/bodega/cart_helper.rb +9 -25
- data/app/models/bodega/order.rb +113 -11
- data/app/models/bodega/order_product.rb +20 -26
- data/app/models/bodega/product.rb +9 -12
- data/app/views/bodega/orders/_cart.html.erb +26 -0
- data/app/views/bodega/orders/_cart_row.html.erb +24 -0
- data/app/views/bodega/orders/_shipping_row.html.erb +13 -0
- data/app/views/bodega/orders/edit.html.erb +1 -0
- data/app/views/bodega/orders/new.html.erb +2 -38
- data/bodega.gemspec +30 -12
- data/config/locales/en.yml +13 -0
- data/config/routes.rb +9 -9
- data/db/migrate/20121111170337_create_bodega_orders.rb +12 -0
- data/lib/bodega/engine.rb +0 -1
- data/lib/bodega/optional.rb +12 -0
- data/lib/bodega/payment_method/base.rb +5 -13
- data/lib/bodega/payment_method/paypal.rb +12 -4
- data/lib/bodega/payment_method.rb +0 -4
- data/lib/bodega/shipping_method/base.rb +71 -0
- data/lib/bodega/shipping_method/ups.rb +18 -0
- data/lib/bodega/shipping_method.rb +5 -0
- data/lib/bodega.rb +19 -1
- data/spec/lib/bodega/payment_method/base_spec.rb +12 -0
- data/spec/lib/bodega/shipping_method/base_spec.rb +12 -0
- data/spec/lib/bodega_spec.rb +18 -0
- data/spec/models/order_product_spec.rb +72 -0
- data/spec/models/order_spec.rb +93 -0
- data/spec/models/product_spec.rb +78 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/active_record.rb +43 -0
- data/spec/support/rails.rb +7 -0
- data/spec/support/vcr.rb +0 -0
- metadata +36 -18
data/.rspec
CHANGED
data/Gemfile
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
|
3
|
-
gem '
|
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.
|
5
|
-
activemodel (= 3.2.
|
6
|
-
activesupport (= 3.2.
|
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
|
-
|
15
|
-
|
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
|
-
|
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.
|
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.
|
60
|
+
pry (0.9.11.4)
|
44
61
|
coderay (~> 1.0.5)
|
45
62
|
method_source (~> 0.8)
|
46
|
-
slop (~> 3.
|
47
|
-
rack (1.4.
|
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.
|
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.
|
55
|
-
actionpack (= 3.2.
|
56
|
-
activesupport (= 3.2.
|
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.
|
68
|
-
rspec-rails (2.12.
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
1
|
+
0.4.0
|
@@ -1,62 +1,53 @@
|
|
1
1
|
class Bodega::OrdersController < ApplicationController
|
2
|
-
|
3
|
-
include Bodega::PaymentMethod
|
2
|
+
before_filter :find_order, only: %w(show)
|
4
3
|
|
5
|
-
|
4
|
+
helper 'bodega/cart'
|
6
5
|
|
7
6
|
def add
|
8
|
-
if product = params[:
|
7
|
+
if product = params[:order_product]
|
9
8
|
update_cart(product)
|
10
9
|
end
|
11
|
-
redirect_to
|
10
|
+
redirect_to new_order_path
|
12
11
|
end
|
13
12
|
|
14
13
|
def complete
|
15
|
-
if current_order.finalize!(
|
16
|
-
|
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
|
-
|
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[:
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
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
|
-
|
47
|
-
|
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
|
@@ -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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
data/app/models/bodega/order.rb
CHANGED
@@ -1,43 +1,145 @@
|
|
1
|
+
require 'maintain'
|
2
|
+
|
1
3
|
module Bodega
|
2
4
|
class Order < ActiveRecord::Base
|
3
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
27
|
-
|
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
|
31
|
-
|
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
|
-
|
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 :
|
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.
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
"
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
35
|
-
keep_stock? ? number_in_stock :
|
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' %>
|