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.
- 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' %>
|