comable 0.0.2 → 0.0.3

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