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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56d44aba496e45370483a0b7a625fcf96f4dabf2
4
- data.tar.gz: 03b299ed0c7bcfb019964d1da1758175951052d0
3
+ metadata.gz: fa8570f259479b4b4a76b6c53c7b0619f43268e0
4
+ data.tar.gz: 20638bda91ba247e08fb561a27168f8ec67423af
5
5
  SHA512:
6
- metadata.gz: d97538ce5e3a49d8c47b6f98da650be04606d4e51b535fc610ef9b9c334740bd17d9c6c66e4e2931c007851a27ce20eaa0b5bf2d0b718dd649e7bdfb6d326826
7
- data.tar.gz: fd264578ddb1caffef0f74ece4c72d08cd73092250b833101f9708ab34687e489d487c8cf8e5757f0f91183d1316aef9cd9c4d2bd787e6b0a498ef1b0d69e74c
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
- @order = current_customer.order(build_order_nested_attributes)
33
- if @order.valid?
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
- order_attributes = JSON.parse(session['comable.order'] || '{}')
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
- session['comable.order'] = build_order_nested_attributes.to_json
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
- cart_items.destroy_all
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
- Comable::CartItem.where(
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 cart
57
- Cart.new(cart_items)
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
- Comable::CashRegister.new(customer: self, order_attributes: order_params).build_order
48
+ incomplete_order.attributes = order_params
49
+ incomplete_order.precomplete
69
50
  end
70
51
 
71
52
  def order(order_params = {})
72
- Comable::CashRegister.new(customer: self, order_attributes: order_params).create_order
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 process_cart_item(obj)
83
- case obj
84
- when Comable::Product
85
- yield obj.stocks.first
86
- when Comable::Stock
87
- yield obj
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 add_stock_to_cart(stock, quantity)
96
- fail I18n.t('comable.carts.product_not_stocked') if stock.soldout?
97
-
98
- cart_items = find_cart_items_by(stock)
99
- if cart_items.any?
100
- cart_item = cart_items.first
101
- cart_item.quantity += quantity
102
- (cart_item.quantity > 0) ? cart_item.save : cart_item.destroy
103
- else
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
- def reset_stock_from_cart(stock, quantity)
110
- cart_items = find_cart_items_by(stock)
111
- if quantity > 0
112
- return add_stock_to_cart(stock, quantity) if cart_items.empty?
113
- cart_item = cart_items.first
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
- end
89
+ alias_method_chain :add_stock_to_cart, :reload
121
90
 
122
- def find_cart_items_by(stock)
123
- fail I18n.t('comable.carts.product_not_found') unless stock.is_a?(Comable::Stock)
124
- cart_items.where(Comable::Stock.table_name.singularize.foreign_key => stock.id)
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
@@ -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
- validates :family_name, presence: true
11
- validates :first_name, presence: true
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
- before_create :generate_code
14
- before_create :generate_ordered_at
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 generate_ordered_at
26
- self.ordered_at = Time.now
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
- after_create :decrement_quantity!
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
@@ -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
- def decrement_quantity!
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
- update_attributes!(quantity: quantity.pred)
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
- - if stock.sku?
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
- .quantity
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
@@ -12,9 +12,11 @@ Comable::Engine.routes.draw do
12
12
  resource :order do
13
13
  collection do
14
14
  get :orderer
15
- post :orderer
15
+ put :orderer
16
16
  get :delivery
17
- post :delivery
17
+ put :delivery
18
+ get :payment
19
+ put :payment
18
20
  get :confirm
19
21
  end
20
22
  end
@@ -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.string :code, null: false
6
- t.string :family_name, null: false
7
- t.string :first_name, null: false
8
- t.datetime :ordered_at, null: false
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, null: false
6
- t.string :first_name, null: false
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, null: false
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
@@ -1,8 +1,9 @@
1
1
  require 'comable/engine'
2
2
  require 'comable/migration'
3
3
  require 'comable/decoratable'
4
-
5
- require 'comable/cash_register'
4
+ require 'comable/errors'
5
+ require 'comable/cart_owner'
6
+ require 'comable/payment_method'
6
7
 
7
8
  module Comable
8
9
  end
@@ -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,4 @@
1
+ module Comable
2
+ class InvalidOrder < StandardError
3
+ end
4
+ 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
@@ -0,0 +1,15 @@
1
+ module Comable
2
+ module PaymentMethod
3
+ class General < Base
4
+ class << self
5
+ def display_name
6
+ '汎用決済モジュール'
7
+ end
8
+
9
+ def kind
10
+ { none: 'なし' }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module Comable
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
@@ -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.2
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-08 00:00:00.000000000 Z
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/cash_register.rb
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