comable 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/comable/orders_controller.rb +35 -28
- data/app/models/comable/customer.rb +41 -71
- data/app/models/comable/order.rb +70 -6
- data/app/models/comable/order_delivery.rb +6 -2
- data/app/models/comable/order_detail.rb +31 -2
- data/app/models/comable/payment.rb +26 -0
- data/app/models/comable/stock.rb +53 -2
- data/app/views/comable/carts/show.slim +1 -6
- data/app/views/comable/orders/confirm.slim +2 -2
- data/app/views/comable/orders/delivery.slim +1 -1
- data/app/views/comable/orders/orderer.slim +1 -1
- data/app/views/comable/orders/payment.slim +12 -0
- data/config/routes.rb +4 -2
- data/db/migrate/20140723175431_create_comable_orders.rb +6 -4
- data/db/migrate/20140723175624_create_comable_order_deliveries.rb +2 -2
- data/db/migrate/20140723175810_create_comable_order_details.rb +3 -1
- data/db/migrate/20140817194104_create_comable_payments.rb +11 -0
- data/lib/comable.rb +3 -2
- data/lib/comable/cart_owner.rb +79 -0
- data/lib/comable/errors.rb +4 -0
- data/lib/comable/payment_method.rb +14 -0
- data/lib/comable/payment_method/base.rb +26 -0
- data/lib/comable/payment_method/general.rb +15 -0
- data/lib/comable/version.rb +1 -1
- data/lib/tasks/comable_yardoc.task +14 -0
- metadata +40 -5
- data/app/models/comable/cart_item.rb +0 -34
- data/db/migrate/20140120024159_create_comable_cart_items.rb +0 -12
- data/lib/comable/cash_register.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa8570f259479b4b4a76b6c53c7b0619f43268e0
|
4
|
+
data.tar.gz: 20638bda91ba247e08fb561a27168f8ec67423af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbb7f75de40ca35dfc593a5a78bcc7808e0d183667a40c78cb36e2d097485125c9425bbee8d8d9207cf7fb01a3263c7fc6b01e3dab0b141f0b94905985d95e66
|
7
|
+
data.tar.gz: 3f2ccc2dd66370b11f1dfa8651b3e1a7052a18b5e953ecc6ad3cc4ec0db03c7ed878f64cc2cd246591a780d0a90e49ffe6f8cfdf47e26ff47c6b2620b160a427
|
@@ -5,6 +5,11 @@ module Comable
|
|
5
5
|
before_filter :redirect_for_logged_in_customer, only: [:new, :orderer]
|
6
6
|
after_filter :save_order, except: :create
|
7
7
|
|
8
|
+
rescue_from ActiveRecord::RecordInvalid, with: :record_invalid
|
9
|
+
rescue_from Comable::InvalidOrder, with: :order_invalid
|
10
|
+
|
11
|
+
include Decoratable
|
12
|
+
|
8
13
|
def new
|
9
14
|
end
|
10
15
|
|
@@ -16,23 +21,26 @@ module Comable
|
|
16
21
|
end
|
17
22
|
|
18
23
|
def delivery
|
24
|
+
case request.method_symbol
|
25
|
+
when :post
|
26
|
+
redirect_to comable.payment_order_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def payment
|
19
31
|
case request.method_symbol
|
20
32
|
when :post
|
21
33
|
redirect_to comable.confirm_order_path
|
22
|
-
when :get
|
23
|
-
@order.order_deliveries.build if @order.order_deliveries.empty?
|
24
34
|
end
|
25
35
|
end
|
26
36
|
|
27
37
|
def confirm
|
28
|
-
@order = current_customer.preorder(build_order_nested_attributes)
|
29
38
|
end
|
30
39
|
|
31
40
|
def create
|
32
|
-
|
33
|
-
if
|
41
|
+
order = current_customer.order
|
42
|
+
if order.complete?
|
34
43
|
flash[:notice] = I18n.t('comable.orders.success')
|
35
|
-
reset_session
|
36
44
|
else
|
37
45
|
flash[:alert] = I18n.t('comable.orders.failure')
|
38
46
|
redirect_to comable.confirm_order_path
|
@@ -41,10 +49,6 @@ module Comable
|
|
41
49
|
|
42
50
|
private
|
43
51
|
|
44
|
-
def reset_session
|
45
|
-
session.delete('comable.order')
|
46
|
-
end
|
47
|
-
|
48
52
|
def verify
|
49
53
|
return if current_customer.cart.any?
|
50
54
|
flash[:alert] = I18n.t('comable.carts.empty')
|
@@ -52,27 +56,11 @@ module Comable
|
|
52
56
|
end
|
53
57
|
|
54
58
|
def load_order
|
55
|
-
|
56
|
-
order_attributes.update(order_params) if order_params
|
57
|
-
@order = Comable::Order.new(order_attributes)
|
59
|
+
@order = current_customer.preorder(order_params || {})
|
58
60
|
end
|
59
61
|
|
60
62
|
def save_order
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
def build_order_nested_attributes
|
65
|
-
@order.attributes.merge(
|
66
|
-
order_deliveries_attributes: build_order_delivery_nested_attributes
|
67
|
-
)
|
68
|
-
end
|
69
|
-
|
70
|
-
def build_order_delivery_nested_attributes
|
71
|
-
@order.order_deliveries.map do |order_delivery|
|
72
|
-
order_delivery.attributes.merge(
|
73
|
-
order_details_attributes: order_delivery.order_details.map(&:attributes)
|
74
|
-
)
|
75
|
-
end
|
63
|
+
@order.save
|
76
64
|
end
|
77
65
|
|
78
66
|
def order_params
|
@@ -82,6 +70,8 @@ module Comable
|
|
82
70
|
order_params_for_orderer
|
83
71
|
when :delivery
|
84
72
|
order_params_for_delivery
|
73
|
+
when :payment
|
74
|
+
order_params_for_payment
|
85
75
|
end
|
86
76
|
end
|
87
77
|
|
@@ -95,14 +85,31 @@ module Comable
|
|
95
85
|
def order_params_for_delivery
|
96
86
|
params.require(:order).permit(
|
97
87
|
order_deliveries_attributes: [
|
88
|
+
:id,
|
98
89
|
:family_name,
|
99
90
|
:first_name
|
100
91
|
]
|
101
92
|
)
|
102
93
|
end
|
103
94
|
|
95
|
+
def order_params_for_payment
|
96
|
+
params.require(:order).permit(
|
97
|
+
Comable::Payment.table_name.singularize.foreign_key.to_sym
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
104
101
|
def redirect_for_logged_in_customer
|
105
102
|
return redirect_to delivery_order_path if current_customer.logged_in?
|
106
103
|
end
|
104
|
+
|
105
|
+
def record_invalid
|
106
|
+
flash[:alert] = I18n.t('comable.orders.failure')
|
107
|
+
redirect_to comable.cart_path
|
108
|
+
end
|
109
|
+
|
110
|
+
def order_invalid(exception)
|
111
|
+
flash[:alert] = exception.message
|
112
|
+
redirect_to comable.cart_path
|
113
|
+
end
|
107
114
|
end
|
108
115
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Comable
|
2
2
|
class Customer < ActiveRecord::Base
|
3
3
|
include Decoratable
|
4
|
+
include CartOwner
|
4
5
|
|
5
6
|
has_many :comable_orders, class_name: Comable::Order.name, foreign_key: table_name.singularize.foreign_key
|
6
7
|
alias_method :orders, :comable_orders
|
@@ -24,52 +25,33 @@ module Comable
|
|
24
25
|
!logged_in?
|
25
26
|
end
|
26
27
|
|
27
|
-
def add_cart_item(obj, quantity: 1)
|
28
|
-
process_cart_item(obj) do |stock|
|
29
|
-
add_stock_to_cart(stock, quantity)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def remove_cart_item(obj, quantity: -1)
|
34
|
-
process_cart_item(obj) do |stock|
|
35
|
-
add_stock_to_cart(stock, quantity)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def reset_cart_item(obj, quantity: 0)
|
40
|
-
process_cart_item(obj) do |stock|
|
41
|
-
reset_stock_from_cart(stock, quantity)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
28
|
def reset_cart
|
46
|
-
|
29
|
+
return unless @incomplete_order
|
30
|
+
if @incomplete_order.complete?
|
31
|
+
@incomplete_order = nil
|
32
|
+
else
|
33
|
+
# TODO: テストケースの作成
|
34
|
+
@incomplete_order.destroy
|
35
|
+
@incomplete_order = nil
|
36
|
+
end
|
47
37
|
end
|
48
38
|
|
49
39
|
def cart_items
|
50
|
-
|
51
|
-
Comable::Customer.table_name.singularize.foreign_key => id,
|
52
|
-
:guest_token => current_guest_token
|
53
|
-
)
|
40
|
+
incomplete_order.order_deliveries.first.order_details
|
54
41
|
end
|
55
42
|
|
56
|
-
def
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
class Cart < Array
|
61
|
-
def price
|
62
|
-
cart_item_ids = map(&:id)
|
63
|
-
Comable::CartItem.includes(stock: :product).where(id: cart_item_ids).to_a.sum(&:price)
|
64
|
-
end
|
43
|
+
def incomplete_order
|
44
|
+
@incomplete_order ||= initialize_incomplete_order
|
65
45
|
end
|
66
46
|
|
67
47
|
def preorder(order_params = {})
|
68
|
-
|
48
|
+
incomplete_order.attributes = order_params
|
49
|
+
incomplete_order.precomplete
|
69
50
|
end
|
70
51
|
|
71
52
|
def order(order_params = {})
|
72
|
-
|
53
|
+
incomplete_order.attributes = order_params
|
54
|
+
incomplete_order.complete.tap { |completed_flag| reset_cart if completed_flag }
|
73
55
|
end
|
74
56
|
|
75
57
|
private
|
@@ -79,49 +61,37 @@ module Comable
|
|
79
61
|
@cookies.signed[:guest_token]
|
80
62
|
end
|
81
63
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
when Array
|
89
|
-
obj.map { |item| yield item }
|
90
|
-
else
|
91
|
-
fail
|
92
|
-
end
|
64
|
+
def initialize_incomplete_order
|
65
|
+
orders = find_incomplete_orders
|
66
|
+
return orders.first if orders.any?
|
67
|
+
order = orders.create(family_name: family_name, first_name: first_name, order_deliveries_attributes: [{ family_name: family_name, first_name: first_name }])
|
68
|
+
@cookies.permanent.signed[:guest_token] = order.guest_token if not_logged_in?
|
69
|
+
order
|
93
70
|
end
|
94
71
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
cart_item = cart_items.create(quantity: quantity)
|
105
|
-
@cookies.permanent.signed[:guest_token] = cart_item.guest_token if not_logged_in?
|
106
|
-
end
|
72
|
+
def find_incomplete_orders
|
73
|
+
Comable::Order
|
74
|
+
.incomplete
|
75
|
+
.includes(order_deliveries: :order_details)
|
76
|
+
.where(
|
77
|
+
Comable::Customer.table_name.singularize.foreign_key => self,
|
78
|
+
:guest_token => current_guest_token
|
79
|
+
)
|
80
|
+
.limit(1)
|
107
81
|
end
|
108
82
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
cart_item.quantity = quantity
|
115
|
-
cart_item.save
|
116
|
-
else
|
117
|
-
return false if cart_items.empty?
|
118
|
-
cart_item.destroy
|
83
|
+
# Rails 3.x だと has_many 先のインスタンスが追加されても
|
84
|
+
# 親インスタンスが感知できないので、いちいちリロードするように変更
|
85
|
+
if Rails::VERSION::MAJOR == 3
|
86
|
+
def add_stock_to_cart_with_reload(*args)
|
87
|
+
add_stock_to_cart_without_reload(*args).tap { @incomplete_order = nil }
|
119
88
|
end
|
120
|
-
|
89
|
+
alias_method_chain :add_stock_to_cart, :reload
|
121
90
|
|
122
|
-
|
123
|
-
|
124
|
-
|
91
|
+
def reset_stock_from_cart_with_reload(*args)
|
92
|
+
reset_stock_from_cart_without_reload(*args).tap { @incomplete_order = nil }
|
93
|
+
end
|
94
|
+
alias_method_chain :reset_stock_from_cart, :reload
|
125
95
|
end
|
126
96
|
end
|
127
97
|
end
|
data/app/models/comable/order.rb
CHANGED
@@ -3,18 +3,78 @@ module Comable
|
|
3
3
|
include Decoratable
|
4
4
|
|
5
5
|
belongs_to :customer, class_name: Comable::Customer.name, foreign_key: Comable::Customer.table_name.singularize.foreign_key, autosave: false
|
6
|
+
belongs_to :payment, class_name: Comable::Payment.name, foreign_key: Comable::Payment.table_name.singularize.foreign_key, autosave: false
|
6
7
|
has_many :order_deliveries, dependent: :destroy, class_name: Comable::OrderDelivery.name, foreign_key: table_name.singularize.foreign_key
|
7
8
|
|
8
9
|
accepts_nested_attributes_for :order_deliveries
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
with_options if: :complete? do |complete_order|
|
12
|
+
complete_order.validates :code, presence: true
|
13
|
+
complete_order.validates :first_name, presence: true
|
14
|
+
complete_order.validates :family_name, presence: true
|
15
|
+
complete_order.validates Comable::Payment.table_name.singularize.foreign_key, presence: true
|
16
|
+
end
|
17
|
+
|
18
|
+
with_options if: :incomplete? do |incomplete_order|
|
19
|
+
incomplete_order.validates Comable::Customer.table_name.singularize.foreign_key, uniqueness: { scope: [Comable::Customer.table_name.singularize.foreign_key, :completed_at] }, if: :customer
|
20
|
+
incomplete_order.validates :guest_token, uniqueness: { scope: [:guest_token, :completed_at] }, if: :guest_token
|
21
|
+
end
|
22
|
+
|
23
|
+
before_create :generate_guest_token
|
24
|
+
|
25
|
+
scope :complete, -> { where.not(completed_at: nil) }
|
26
|
+
scope :incomplete, -> { where(completed_at: nil) }
|
27
|
+
|
28
|
+
def precomplete
|
29
|
+
valid_stock
|
30
|
+
fail Comable::InvalidOrder, errors.full_messages.join("\n") if errors.any?
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def complete
|
35
|
+
# TODO: トランザクションの追加
|
36
|
+
precomplete
|
37
|
+
# TODO: コールバック化
|
38
|
+
# define_model_callbacks :complete
|
39
|
+
before_complete
|
40
|
+
save!
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def complete?
|
45
|
+
!incomplete?
|
46
|
+
end
|
12
47
|
|
13
|
-
|
14
|
-
|
48
|
+
def incomplete?
|
49
|
+
completed_at.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
# 時価合計を取得
|
53
|
+
def current_total_price
|
54
|
+
order_deliveries.map(&:order_details).flatten.each(&:current_subtotal_price)
|
55
|
+
end
|
56
|
+
|
57
|
+
# 売価合計を取得
|
58
|
+
def total_price
|
59
|
+
order_deliveries.map(&:order_details).flatten.each(&:subtotal_price)
|
60
|
+
end
|
15
61
|
|
16
62
|
private
|
17
63
|
|
64
|
+
def before_complete
|
65
|
+
self.completed_at = Time.now
|
66
|
+
generate_code
|
67
|
+
order_deliveries.each(&:before_complete)
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid_stock
|
71
|
+
order_deliveries.map(&:order_details).flatten.each do |order_detail|
|
72
|
+
return errors.add :base, "「#{order_detail.stock.name}」の注文数が不正です。" if order_detail.quantity <= 0
|
73
|
+
quantity = order_detail.stock.quantity - order_detail.quantity
|
74
|
+
return errors.add :base, "「#{order_detail.stock.name}」の在庫が不足しています。" if quantity < 0
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
18
78
|
def generate_code
|
19
79
|
self.code = loop do
|
20
80
|
random_token = "C#{Array.new(11) { rand(9) }.join}"
|
@@ -22,8 +82,12 @@ module Comable
|
|
22
82
|
end
|
23
83
|
end
|
24
84
|
|
25
|
-
def
|
26
|
-
|
85
|
+
def generate_guest_token
|
86
|
+
return if send(Comable::Customer.table_name.singularize.foreign_key)
|
87
|
+
self.guest_token ||= loop do
|
88
|
+
random_token = SecureRandom.urlsafe_base64(nil, false)
|
89
|
+
break random_token unless self.class.exists?(guest_token: random_token)
|
90
|
+
end
|
27
91
|
end
|
28
92
|
end
|
29
93
|
end
|
@@ -5,8 +5,12 @@ module Comable
|
|
5
5
|
belongs_to :order, class_name: Comable::Order.name, foreign_key: Comable::Order.table_name.singularize.foreign_key
|
6
6
|
has_many :order_details, dependent: :destroy, class_name: Comable::OrderDetail.name, foreign_key: table_name.singularize.foreign_key
|
7
7
|
|
8
|
-
accepts_nested_attributes_for :order_details
|
9
|
-
|
10
8
|
delegate :customer, to: :order
|
9
|
+
delegate :guest_token, to: :order
|
10
|
+
delegate :complete?, to: :order
|
11
|
+
|
12
|
+
def before_complete
|
13
|
+
order_details.each(&:before_complete)
|
14
|
+
end
|
11
15
|
end
|
12
16
|
end
|
@@ -5,9 +5,38 @@ module Comable
|
|
5
5
|
belongs_to :stock, class_name: Comable::Stock.name, foreign_key: Comable::Stock.table_name.singularize.foreign_key
|
6
6
|
belongs_to :order_delivery, class_name: Comable::OrderDelivery.name, foreign_key: Comable::OrderDelivery.table_name.singularize.foreign_key
|
7
7
|
|
8
|
-
|
9
|
-
delegate :decrement_quantity!, to: :stock
|
8
|
+
# TODO: バリデーションの追加
|
10
9
|
|
11
10
|
delegate :product, to: :stock
|
11
|
+
delegate :guest_token, to: :order_delivery
|
12
|
+
delegate :complete?, to: :order_delivery
|
13
|
+
|
14
|
+
def before_complete
|
15
|
+
save_price
|
16
|
+
decrement_stock
|
17
|
+
end
|
18
|
+
|
19
|
+
# 時価を取得
|
20
|
+
def current_price
|
21
|
+
stock.price
|
22
|
+
end
|
23
|
+
|
24
|
+
# 時価小計を取得
|
25
|
+
def current_subtotal_price
|
26
|
+
current_price * quantity
|
27
|
+
end
|
28
|
+
|
29
|
+
# 売価小計を取得
|
30
|
+
def subtotal_price
|
31
|
+
price * quantity
|
32
|
+
end
|
33
|
+
|
34
|
+
def decrement_stock
|
35
|
+
stock.decrement!(quantity: quantity)
|
36
|
+
end
|
37
|
+
|
38
|
+
def save_price
|
39
|
+
self.price = stock.price
|
40
|
+
end
|
12
41
|
end
|
13
42
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Comable
|
2
|
+
class Payment < ActiveRecord::Base
|
3
|
+
include Decoratable
|
4
|
+
|
5
|
+
validates :name, presence: true
|
6
|
+
validates :payment_method_type, presence: true
|
7
|
+
validates :payment_method_kind, presence: true
|
8
|
+
|
9
|
+
def payment_method
|
10
|
+
return unless Object.const_defined?(payment_method_type)
|
11
|
+
Object.const_get(payment_method_type)
|
12
|
+
end
|
13
|
+
|
14
|
+
def payment_method_name
|
15
|
+
payment_method.display_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def payment_method_kind_key
|
19
|
+
payment_method.kind.keys.slice(payment_method_kind)
|
20
|
+
end
|
21
|
+
|
22
|
+
def payment_method_kind_name
|
23
|
+
payment_method.kind.slice(payment_method_kind_key).values.first
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/app/models/comable/stock.rb
CHANGED
@@ -1,30 +1,81 @@
|
|
1
1
|
module Comable
|
2
|
+
#
|
3
|
+
# 在庫モデル。
|
4
|
+
# 商品に複数紐付き、品数やSKU(Stock Keeping Unit)情報を保持する。
|
5
|
+
#
|
2
6
|
class Stock < ActiveRecord::Base
|
3
7
|
include Decoratable
|
4
8
|
|
5
9
|
belongs_to :product, class_name: Comable::Product.name, foreign_key: Comable::Product.table_name.singularize.foreign_key
|
6
10
|
|
11
|
+
#
|
12
|
+
# @!group Scope
|
13
|
+
#
|
14
|
+
|
15
|
+
# @!scope class
|
16
|
+
# 有効な在庫インスタンスを返す
|
7
17
|
scope :activated, -> { where.not(product_id_num: nil) }
|
18
|
+
|
19
|
+
# @!scope class
|
20
|
+
# 品切れでない在庫インスタンスを返す
|
8
21
|
scope :unsold, -> { where('quantity > ?', 0) }
|
22
|
+
|
23
|
+
# @!scope class
|
24
|
+
# 品切れの在庫インスタンスを返す
|
9
25
|
scope :soldout, -> { where('quantity <= ?', 0) }
|
10
26
|
|
27
|
+
#
|
28
|
+
# @!endgroup
|
29
|
+
#
|
30
|
+
|
11
31
|
delegate :price, to: :product
|
12
32
|
delegate :sku?, to: :product
|
13
33
|
|
34
|
+
def name
|
35
|
+
return product.name unless product.sku?
|
36
|
+
sku_name = sku_h_choice_name
|
37
|
+
sku_name += '/' + sku_v_choice_name if sku_v_choice_name.present?
|
38
|
+
product.name + "(#{sku_name})"
|
39
|
+
end
|
40
|
+
|
41
|
+
# 在庫の有無を取得する
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# stock.unsold? #=> true
|
45
|
+
#
|
46
|
+
# @return [Boolean] 在庫があれば true を返す
|
47
|
+
# @see #soldout?
|
14
48
|
def unsold?
|
15
49
|
return false if product_id_num.nil?
|
16
50
|
return false if quantity.nil?
|
17
51
|
quantity > 0
|
18
52
|
end
|
19
53
|
|
54
|
+
# 在庫の有無を取得する
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
# stock.soldout? #=> false
|
58
|
+
#
|
59
|
+
# @return [Boolean] {#unsold?} の逆。在庫がなければ true を返す
|
60
|
+
# @see #unsold?
|
20
61
|
def soldout?
|
21
62
|
!unsold?
|
22
63
|
end
|
23
64
|
|
24
|
-
|
65
|
+
# 在庫減算を行う
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# stock.quantity #=> 10
|
69
|
+
# stock.decrement!(quantity: 1) #=> true
|
70
|
+
# stock.quantity #=> 9
|
71
|
+
#
|
72
|
+
# @param quantity [Fixnum] 減算する在庫数を指定する
|
73
|
+
# @return [Boolean] レコードの保存に成功すると true を返す
|
74
|
+
def decrement!(quantity: 1)
|
25
75
|
with_lock do
|
26
76
|
# TODO: カラムマッピングのdecrementメソッドへの対応
|
27
|
-
|
77
|
+
self.quantity -= quantity
|
78
|
+
save!
|
28
79
|
end
|
29
80
|
end
|
30
81
|
end
|
@@ -7,12 +7,7 @@ ul.cart
|
|
7
7
|
- product = stock.product
|
8
8
|
li.product
|
9
9
|
h2.name
|
10
|
-
|
11
|
-
- sku_name = stock.sku_h_choice_name
|
12
|
-
- sku_name += '/' + stock.sku_v_choice_name if stock.sku_v_choice_name.present?
|
13
|
-
= link_to product.name + "(#{sku_name})", comable.product_path(product)
|
14
|
-
- else
|
15
|
-
= link_to product.name, comable.product_path(product)
|
10
|
+
= link_to stock.name, comable.product_path(product)
|
16
11
|
.caption
|
17
12
|
= product.caption
|
18
13
|
.price
|
@@ -16,10 +16,10 @@ h1 注文情報確認
|
|
16
16
|
- order_delivery.order_details.each do |order_detail|
|
17
17
|
.name
|
18
18
|
= order_detail.product.name
|
19
|
-
.
|
19
|
+
.price
|
20
20
|
= order_detail.price
|
21
21
|
.quantity
|
22
22
|
= order_detail.quantity
|
23
23
|
|
24
|
-
= form_for @order, as: :order, url: comable.order_path do |f|
|
24
|
+
= form_for @order, as: :order, url: comable.order_path, method: :post do |f|
|
25
25
|
= f.submit
|
@@ -1,7 +1,7 @@
|
|
1
1
|
h1 配送先情報入力
|
2
2
|
|
3
3
|
.orderer
|
4
|
-
= form_for @order, as: :order, url: comable.delivery_order_path do |f|
|
4
|
+
= form_for @order, as: :order, url: comable.delivery_order_path, method: :put do |f|
|
5
5
|
= f.fields_for :order_deliveries do |order_delivery|
|
6
6
|
= order_delivery.text_field :family_name, placeholder: '姓'
|
7
7
|
= order_delivery.text_field :first_name, placeholder: '名'
|
@@ -1,7 +1,7 @@
|
|
1
1
|
h1 注文者情報入力
|
2
2
|
|
3
3
|
.orderer
|
4
|
-
= form_for @order, as: :order, url: comable.orderer_order_path do |f|
|
4
|
+
= form_for @order, as: :order, url: comable.orderer_order_path, method: :put do |f|
|
5
5
|
= f.text_field :family_name, placeholder: '姓'
|
6
6
|
= f.text_field :first_name, placeholder: '名'
|
7
7
|
= f.submit
|
@@ -0,0 +1,12 @@
|
|
1
|
+
h1 決済方法
|
2
|
+
|
3
|
+
.order.payment
|
4
|
+
= form_for @order, as: :order, url: comable.payment_order_path, method: :put do |f|
|
5
|
+
ul
|
6
|
+
- Comable::Payment.all.each.with_index do |payment, index|
|
7
|
+
li
|
8
|
+
- payment_foreign_key = Comable::Payment.table_name.singularize.foreign_key.to_sym
|
9
|
+
- checked_flag = @order.payment ? (@order.payment.id == payment.id) : index.zero?
|
10
|
+
= f.radio_button payment_foreign_key, payment.id, checked: checked_flag
|
11
|
+
= f.label payment_foreign_key, payment.name, value: payment.id
|
12
|
+
= f.submit
|
data/config/routes.rb
CHANGED
@@ -2,10 +2,12 @@ class CreateComableOrders < Comable::Migration
|
|
2
2
|
def change
|
3
3
|
create_table :comable_orders do |t|
|
4
4
|
t.integer :comable_customer_id
|
5
|
-
t.
|
6
|
-
t.string :
|
7
|
-
t.string :
|
8
|
-
t.
|
5
|
+
t.integer :comable_payment_id
|
6
|
+
t.string :guest_token
|
7
|
+
t.string :code
|
8
|
+
t.string :family_name
|
9
|
+
t.string :first_name
|
10
|
+
t.datetime :completed_at
|
9
11
|
end
|
10
12
|
|
11
13
|
add_index :comable_orders, :code, unique: true, name: :comable_orders_idx_01
|
@@ -2,8 +2,8 @@ class CreateComableOrderDeliveries < Comable::Migration
|
|
2
2
|
def change
|
3
3
|
create_table :comable_order_deliveries do |t|
|
4
4
|
t.integer :comable_order_id, null: false
|
5
|
-
t.string :family_name
|
6
|
-
t.string :first_name
|
5
|
+
t.string :family_name
|
6
|
+
t.string :first_name
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -3,8 +3,10 @@ class CreateComableOrderDetails < Comable::Migration
|
|
3
3
|
create_table :comable_order_details do |t|
|
4
4
|
t.integer :comable_order_delivery_id, null: false
|
5
5
|
t.integer :comable_stock_id, null: false
|
6
|
-
t.integer :price
|
6
|
+
t.integer :price
|
7
7
|
t.integer :quantity, default: 1, null: false
|
8
8
|
end
|
9
|
+
|
10
|
+
add_index :comable_order_details, [:comable_order_delivery_id, :comable_stock_id], unique: true, name: :comable_order_details_idx_01
|
9
11
|
end
|
10
12
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateComablePayments < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :comable_payments do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.string :payment_method_type, null: false
|
6
|
+
t.integer :payment_method_kind, null: false
|
7
|
+
t.integer :enable_price_from
|
8
|
+
t.integer :enable_price_to
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/comable.rb
CHANGED
@@ -0,0 +1,79 @@
|
|
1
|
+
module Comable
|
2
|
+
module CartOwner
|
3
|
+
def add_cart_item(obj, quantity: 1)
|
4
|
+
process_cart_item(obj) do |stock|
|
5
|
+
add_stock_to_cart(stock, quantity)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def remove_cart_item(obj, quantity: -1)
|
10
|
+
add_cart_item(obj, quantity: quantity)
|
11
|
+
end
|
12
|
+
|
13
|
+
def reset_cart_item(obj, quantity: 0)
|
14
|
+
process_cart_item(obj) do |stock|
|
15
|
+
reset_stock_from_cart(stock, quantity)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def cart_items
|
20
|
+
fail 'You should implement cart_items method.'
|
21
|
+
end
|
22
|
+
|
23
|
+
def cart
|
24
|
+
Cart.new(cart_items)
|
25
|
+
end
|
26
|
+
|
27
|
+
class Cart < Array
|
28
|
+
def price
|
29
|
+
sum(&:current_subtotal_price)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def process_cart_item(obj)
|
36
|
+
case obj
|
37
|
+
when Comable::Product
|
38
|
+
yield obj.stocks.first
|
39
|
+
when Comable::Stock
|
40
|
+
yield obj
|
41
|
+
when Array
|
42
|
+
obj.map { |item| yield item }
|
43
|
+
else
|
44
|
+
fail
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_stock_to_cart(stock, quantity)
|
49
|
+
fail I18n.t('comable.carts.product_not_stocked') if stock.soldout?
|
50
|
+
|
51
|
+
cart_items = find_cart_items_by(stock)
|
52
|
+
if cart_items.any?
|
53
|
+
cart_item = cart_items.first
|
54
|
+
cart_item.quantity += quantity
|
55
|
+
(cart_item.quantity > 0) ? cart_item.save : cart_item.destroy
|
56
|
+
else
|
57
|
+
cart_items.create(quantity: quantity)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def reset_stock_from_cart(stock, quantity)
|
62
|
+
cart_items = find_cart_items_by(stock)
|
63
|
+
if quantity > 0
|
64
|
+
return add_stock_to_cart(stock, quantity) if cart_items.empty?
|
65
|
+
cart_item = cart_items.first
|
66
|
+
cart_item.quantity = quantity
|
67
|
+
cart_item.save
|
68
|
+
else
|
69
|
+
return false if cart_items.empty?
|
70
|
+
cart_item.destroy
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def find_cart_items_by(stock)
|
75
|
+
fail I18n.t('comable.carts.product_not_found') unless stock.is_a?(Comable::Stock)
|
76
|
+
cart_items.where(Comable::Stock.table_name.singularize.foreign_key => stock.id)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'comable/payment_method/base'
|
2
|
+
require 'comable/payment_method/general'
|
3
|
+
|
4
|
+
module Comable
|
5
|
+
module PaymentMethod
|
6
|
+
class << self
|
7
|
+
def all
|
8
|
+
(constants - [:Base]).map do |constant_name|
|
9
|
+
const_get(constant_name)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Comable
|
2
|
+
module PaymentMethod
|
3
|
+
class Base
|
4
|
+
class << self
|
5
|
+
def name_symbol
|
6
|
+
name.demodulize.underscore.to_sym
|
7
|
+
end
|
8
|
+
|
9
|
+
def display_name
|
10
|
+
please_implement_method
|
11
|
+
end
|
12
|
+
|
13
|
+
def kind
|
14
|
+
please_implement_method
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def please_implement_method
|
20
|
+
calling_method_name = caller_locations(1, 1).first.label
|
21
|
+
fail "You should implement '#{calling_method_name}' method in #{name}."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/comable/version.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'yard'
|
2
|
+
require 'yard/rake/yardoc_task'
|
3
|
+
|
4
|
+
YARD::Rake::YardocTask.new do |t|
|
5
|
+
t.files = %w(
|
6
|
+
app/models/**/*.rb
|
7
|
+
app/controllers/**/*.rb
|
8
|
+
app/helpers/**/*.rb
|
9
|
+
app/mailers/**/*.rb
|
10
|
+
lib/**/*.rb
|
11
|
+
)
|
12
|
+
t.options = []
|
13
|
+
t.options = %w(--debug --verbose) if $trace
|
14
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: comable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- YOSHIDA Hiroki
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -206,6 +206,34 @@ dependencies:
|
|
206
206
|
- - ">="
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: 0.0.6
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: yard
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: inch
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - ">="
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0'
|
230
|
+
type: :development
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - ">="
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '0'
|
209
237
|
description: Comable provides a simple way to add e-commerce features to your Ruby
|
210
238
|
on Rails application.
|
211
239
|
email:
|
@@ -225,11 +253,11 @@ files:
|
|
225
253
|
- app/controllers/comable/products_controller.rb
|
226
254
|
- app/helpers/comable/application_helper.rb
|
227
255
|
- app/helpers/comable/products_helper.rb
|
228
|
-
- app/models/comable/cart_item.rb
|
229
256
|
- app/models/comable/customer.rb
|
230
257
|
- app/models/comable/order.rb
|
231
258
|
- app/models/comable/order_delivery.rb
|
232
259
|
- app/models/comable/order_detail.rb
|
260
|
+
- app/models/comable/payment.rb
|
233
261
|
- app/models/comable/product.rb
|
234
262
|
- app/models/comable/stock.rb
|
235
263
|
- app/views/comable/carts/show.slim
|
@@ -238,25 +266,31 @@ files:
|
|
238
266
|
- app/views/comable/orders/delivery.slim
|
239
267
|
- app/views/comable/orders/new.slim
|
240
268
|
- app/views/comable/orders/orderer.slim
|
269
|
+
- app/views/comable/orders/payment.slim
|
241
270
|
- app/views/comable/products/index.slim
|
242
271
|
- app/views/comable/products/show.slim
|
243
272
|
- app/views/layouts/comable/application.slim
|
244
273
|
- config/locales/ja.yml
|
245
274
|
- config/routes.rb
|
246
275
|
- db/migrate/20131214194807_create_comable_products.rb
|
247
|
-
- db/migrate/20140120024159_create_comable_cart_items.rb
|
248
276
|
- db/migrate/20140120032559_create_comable_customers.rb
|
249
277
|
- db/migrate/20140502060116_create_comable_stocks.rb
|
250
278
|
- db/migrate/20140723175431_create_comable_orders.rb
|
251
279
|
- db/migrate/20140723175624_create_comable_order_deliveries.rb
|
252
280
|
- db/migrate/20140723175810_create_comable_order_details.rb
|
281
|
+
- db/migrate/20140817194104_create_comable_payments.rb
|
253
282
|
- lib/comable.rb
|
254
|
-
- lib/comable/
|
283
|
+
- lib/comable/cart_owner.rb
|
255
284
|
- lib/comable/decoratable.rb
|
256
285
|
- lib/comable/engine.rb
|
286
|
+
- lib/comable/errors.rb
|
257
287
|
- lib/comable/migration.rb
|
288
|
+
- lib/comable/payment_method.rb
|
289
|
+
- lib/comable/payment_method/base.rb
|
290
|
+
- lib/comable/payment_method/general.rb
|
258
291
|
- lib/comable/version.rb
|
259
292
|
- lib/tasks/comable_tasks.rake
|
293
|
+
- lib/tasks/comable_yardoc.task
|
260
294
|
homepage: https://github.com/hyoshida/comable#comable
|
261
295
|
licenses: []
|
262
296
|
metadata: {}
|
@@ -282,3 +316,4 @@ specification_version: 4
|
|
282
316
|
summary: Comable provides a simple way to add e-commerce features to your Ruby on
|
283
317
|
Rails application.
|
284
318
|
test_files: []
|
319
|
+
has_rdoc:
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module Comable
|
2
|
-
class CartItem < ActiveRecord::Base
|
3
|
-
belongs_to :customer, class_name: Comable::Customer.name, foreign_key: Comable::Customer.table_name.singularize.foreign_key
|
4
|
-
belongs_to :stock, class_name: Comable::Stock.name, foreign_key: Comable::Stock.table_name.singularize.foreign_key
|
5
|
-
|
6
|
-
with_options if: :customer do |customer|
|
7
|
-
customer.validates Comable::Customer.table_name.singularize.foreign_key, uniqueness: { scope: [Comable::Customer.table_name.singularize.foreign_key, Comable::Stock.table_name.singularize.foreign_key] }
|
8
|
-
customer.validates Comable::Customer.table_name.singularize.foreign_key, presence: true
|
9
|
-
end
|
10
|
-
|
11
|
-
with_options if: :guest_token do |guest|
|
12
|
-
guest.validates :guest_token, uniqueness: { scope: [:guest_token, Comable::Stock.table_name.singularize.foreign_key] }
|
13
|
-
guest.validates :guest_token, presence: true
|
14
|
-
end
|
15
|
-
|
16
|
-
delegate :product, to: :stock
|
17
|
-
|
18
|
-
before_create :generate_guest_token
|
19
|
-
|
20
|
-
def price
|
21
|
-
stock.price * quantity
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def generate_guest_token
|
27
|
-
return if send(Comable::Customer.table_name.singularize.foreign_key)
|
28
|
-
self.guest_token ||= loop do
|
29
|
-
random_token = SecureRandom.urlsafe_base64(nil, false)
|
30
|
-
break random_token unless self.class.exists?(guest_token: random_token)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
class CreateComableCartItems < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :comable_cart_items do |t|
|
4
|
-
t.integer :comable_customer_id
|
5
|
-
t.integer :comable_stock_id, null: false
|
6
|
-
t.integer :quantity, default: 1, null: false
|
7
|
-
t.string :guest_token
|
8
|
-
end
|
9
|
-
|
10
|
-
add_index :comable_cart_items, [:comable_customer_id, :comable_stock_id], unique: true, name: :comable_cart_items_idx_01
|
11
|
-
end
|
12
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
module Comable
|
2
|
-
class InvalidOrder < StandardError; end
|
3
|
-
|
4
|
-
class CashRegister
|
5
|
-
attr_accessor :customer
|
6
|
-
attr_accessor :order
|
7
|
-
|
8
|
-
def initialize(attributes)
|
9
|
-
@customer = attributes[:customer]
|
10
|
-
@order = @customer.orders.build(attributes[:order_attributes])
|
11
|
-
end
|
12
|
-
|
13
|
-
def build_order
|
14
|
-
assign_default_attributes_to_order
|
15
|
-
fail Comable::InvalidOrder if invalid
|
16
|
-
order
|
17
|
-
end
|
18
|
-
|
19
|
-
def create_order
|
20
|
-
order = build_order
|
21
|
-
order.save
|
22
|
-
customer.reset_cart
|
23
|
-
order
|
24
|
-
end
|
25
|
-
|
26
|
-
def valid
|
27
|
-
valid_cart && valid_stock
|
28
|
-
end
|
29
|
-
|
30
|
-
def invalid
|
31
|
-
!valid
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def valid_cart
|
37
|
-
cart = customer.cart
|
38
|
-
|
39
|
-
order.order_deliveries.map(&:order_details).flatten.each do |order_detail|
|
40
|
-
next unless order_detail.stock
|
41
|
-
result = cart.reject! { |cart_item| cart_item.stock == order_detail.stock }
|
42
|
-
return false if result.nil?
|
43
|
-
end
|
44
|
-
|
45
|
-
cart.empty?
|
46
|
-
end
|
47
|
-
|
48
|
-
def valid_stock
|
49
|
-
order.order_deliveries.map(&:order_details).flatten.each do |order_detail|
|
50
|
-
next unless order_detail.stock
|
51
|
-
return false unless order_detail.stock.unsold?
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def assign_default_attributes_to_order
|
56
|
-
order.family_name ||= customer.family_name
|
57
|
-
order.first_name ||= customer.first_name
|
58
|
-
|
59
|
-
order.order_deliveries.build if order.order_deliveries.empty?
|
60
|
-
assign_default_attributes_to_order_deliveries
|
61
|
-
end
|
62
|
-
|
63
|
-
def assign_default_attributes_to_order_deliveries
|
64
|
-
order.order_deliveries.each do |order_delivery|
|
65
|
-
assign_default_attributes_to_order_delivery(order_delivery)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def assign_default_attributes_to_order_delivery(order_delivery)
|
70
|
-
order_delivery.family_name ||= customer.family_name
|
71
|
-
order_delivery.first_name ||= customer.first_name
|
72
|
-
|
73
|
-
assign_default_attributes_to_order_details(order_delivery) if first_order_detail?
|
74
|
-
end
|
75
|
-
|
76
|
-
def assign_default_attributes_to_order_details(order_delivery)
|
77
|
-
customer.cart.each do |cart_item|
|
78
|
-
order_delivery.order_details.build(
|
79
|
-
Comable::Stock.table_name.singularize.foreign_key => cart_item.stock.id,
|
80
|
-
:quantity => cart_item.quantity,
|
81
|
-
:price => cart_item.price
|
82
|
-
)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def first_order_detail?
|
87
|
-
order.order_deliveries.map(&:order_details).all?(&:empty?)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|