comable_core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Rakefile +42 -0
- data/app/assets/javascripts/comable/application.js +13 -0
- data/app/assets/stylesheets/comable/application.css +13 -0
- data/app/helpers/comable/application_helper.rb +18 -0
- data/app/helpers/comable/products_helper.rb +55 -0
- data/app/models/comable/customer.rb +95 -0
- data/app/models/comable/order.rb +93 -0
- data/app/models/comable/order_delivery.rb +16 -0
- data/app/models/comable/order_detail.rb +42 -0
- data/app/models/comable/payment.rb +26 -0
- data/app/models/comable/product.rb +32 -0
- data/app/models/comable/stock.rb +82 -0
- data/config/locales/ja.yml +11 -0
- data/db/migrate/20131214194807_create_comable_products.rb +14 -0
- data/db/migrate/20140120032559_create_comable_customers.rb +8 -0
- data/db/migrate/20140502060116_create_comable_stocks.rb +14 -0
- data/db/migrate/20140723175431_create_comable_orders.rb +15 -0
- data/db/migrate/20140723175624_create_comable_order_deliveries.rb +9 -0
- data/db/migrate/20140723175810_create_comable_order_details.rb +12 -0
- data/db/migrate/20140817194104_create_comable_payments.rb +11 -0
- data/lib/comable/cart_owner.rb +79 -0
- data/lib/comable/core/engine.rb +12 -0
- data/lib/comable/decoratable.rb +10 -0
- data/lib/comable/errors.rb +4 -0
- data/lib/comable/payment_method/base.rb +26 -0
- data/lib/comable/payment_method/general.rb +15 -0
- data/lib/comable/payment_method.rb +14 -0
- data/lib/comable_core.rb +9 -0
- data/lib/tasks/comable_tasks.rake +4 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8b1fe7b5731e6c24383b541a3bd2552839440b58
|
4
|
+
data.tar.gz: b9c63920adae0de9f6c12a695e90d69a4a1ff576
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 08063df020955b295602e419a111c870572ee0fa404ea1e1a48ae2d08e5d914da7ce4914939c2089da7ef39a8e85024a29bae5cf958b1362fb0080f267643ae5
|
7
|
+
data.tar.gz: 306fc8020d99c8c8195c3c7d3556008545d7c9d2da04d9457bc51ad34e1ac1948ca4938d7b7f790d9923569150064fe54774686b30708314b3cd60b83c73b09e
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Comable'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
14
|
+
end
|
15
|
+
|
16
|
+
APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
|
17
|
+
load 'rails/tasks/engine.rake'
|
18
|
+
|
19
|
+
Bundler::GemHelper.install_tasks
|
20
|
+
|
21
|
+
# from https://github.com/rspec/rspec-rails/issues/936
|
22
|
+
task 'test:prepare'
|
23
|
+
|
24
|
+
task :rubocop do
|
25
|
+
exec 'rubocop'
|
26
|
+
end
|
27
|
+
|
28
|
+
namespace :app do
|
29
|
+
namespace :spec do
|
30
|
+
desc 'Run the code examples'
|
31
|
+
RSpec::Core::RakeTask.new(:all) do |t|
|
32
|
+
t.pattern << ',backend/spec/**{,/*/**}/*_spec.rb'
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Run the code examples in backend/spec'
|
36
|
+
RSpec::Core::RakeTask.new(:backend) do |t|
|
37
|
+
t.pattern = '../backend/spec/**{,/*/**}/*_spec.rb'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task default: ['app:spec:all', 'rubocop']
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Comable
|
2
|
+
module ApplicationHelper
|
3
|
+
def current_customer
|
4
|
+
@current_customer || load_customer
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def load_customer
|
10
|
+
@current_customer = logged_in_customer
|
11
|
+
@current_customer ||= Comable::Customer.new(cookies)
|
12
|
+
end
|
13
|
+
|
14
|
+
def logged_in_customer
|
15
|
+
# Please override this method for logged in customer
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Comable
|
2
|
+
module ProductsHelper
|
3
|
+
def sku_table(product, options = nil)
|
4
|
+
stocks = product.stocks
|
5
|
+
content_tag(:table, nil, options) do
|
6
|
+
html = ''
|
7
|
+
html << build_sku_v_table_header(product, stocks)
|
8
|
+
html << build_sku_table_rows(product, stocks)
|
9
|
+
html.html_safe
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def build_sku_v_table_header(product, stocks)
|
16
|
+
sku_item_name = product.sku_h_item_name
|
17
|
+
sku_item_name += '/' + product.sku_v_item_name if product.sku_v?
|
18
|
+
|
19
|
+
html = ''
|
20
|
+
html << content_tag(:th, sku_item_name)
|
21
|
+
stocks.group_by(&:sku_h_choice_name).keys.each do |sku_h_choice_name|
|
22
|
+
next if sku_h_choice_name.blank?
|
23
|
+
html << content_tag(:th, sku_h_choice_name)
|
24
|
+
end
|
25
|
+
html.html_safe
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_sku_table_rows(product, stocks)
|
29
|
+
return content_tag(:tr, build_sku_table_row(stocks)) unless product.sku_v?
|
30
|
+
|
31
|
+
html = ''
|
32
|
+
stocks.group_by(&:sku_v_choice_name).each_pair do |sku_v_choice_name, sku_v_stocks|
|
33
|
+
next if sku_v_choice_name.blank?
|
34
|
+
html << content_tag(:tr, build_sku_table_row(sku_v_stocks, sku_v_choice_name))
|
35
|
+
end
|
36
|
+
html.html_safe
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_sku_table_row(stocks, sku_v_choice_name = nil)
|
40
|
+
html = ''
|
41
|
+
html << content_tag(:th, sku_v_choice_name)
|
42
|
+
html << stocks.map { |stock| content_tag(:td, build_sku_product_label(stock)) }.join
|
43
|
+
html.html_safe
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_sku_product_label(stock)
|
47
|
+
content_tag(:label) do
|
48
|
+
html = ''
|
49
|
+
html << radio_button_tag(:stock_id, stock.id, false, disabled: stock.soldout?)
|
50
|
+
html << stock.code
|
51
|
+
html.html_safe
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Comable
|
2
|
+
class Customer < ActiveRecord::Base
|
3
|
+
include Decoratable
|
4
|
+
include CartOwner
|
5
|
+
|
6
|
+
has_many :comable_orders, class_name: Comable::Order.name, foreign_key: table_name.singularize.foreign_key
|
7
|
+
alias_method :orders, :comable_orders
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
obj = args.first
|
11
|
+
case obj.class.name
|
12
|
+
when /Cookies/
|
13
|
+
@cookies = obj
|
14
|
+
super()
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def logged_in?
|
21
|
+
!new_record?
|
22
|
+
end
|
23
|
+
|
24
|
+
def not_logged_in?
|
25
|
+
!logged_in?
|
26
|
+
end
|
27
|
+
|
28
|
+
def reset_cart
|
29
|
+
return unless incomplete_order
|
30
|
+
|
31
|
+
# TODO: テストケースの作成
|
32
|
+
incomplete_order.destroy if incomplete_order.incomplete?
|
33
|
+
|
34
|
+
@incomplete_order = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def cart_items
|
38
|
+
incomplete_order.order_deliveries.first.order_details
|
39
|
+
end
|
40
|
+
|
41
|
+
def incomplete_order
|
42
|
+
@incomplete_order ||= initialize_incomplete_order
|
43
|
+
end
|
44
|
+
|
45
|
+
def preorder(order_params = {})
|
46
|
+
incomplete_order.attributes = order_params
|
47
|
+
incomplete_order.precomplete
|
48
|
+
end
|
49
|
+
|
50
|
+
def order(order_params = {})
|
51
|
+
incomplete_order.attributes = order_params
|
52
|
+
incomplete_order.complete.tap { |completed_flag| reset_cart if completed_flag }
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def current_guest_token
|
58
|
+
return if logged_in?
|
59
|
+
@cookies.signed[:guest_token]
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize_incomplete_order
|
63
|
+
orders = find_incomplete_orders
|
64
|
+
return orders.first if orders.any?
|
65
|
+
order = orders.create(family_name: family_name, first_name: first_name, order_deliveries_attributes: [{ family_name: family_name, first_name: first_name }])
|
66
|
+
@cookies.permanent.signed[:guest_token] = order.guest_token if not_logged_in?
|
67
|
+
order
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_incomplete_orders
|
71
|
+
Comable::Order
|
72
|
+
.incomplete
|
73
|
+
.includes(order_deliveries: :order_details)
|
74
|
+
.where(
|
75
|
+
Comable::Customer.table_name.singularize.foreign_key => self,
|
76
|
+
:guest_token => current_guest_token
|
77
|
+
)
|
78
|
+
.limit(1)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Rails 3.x だと has_many 先のインスタンスが追加されても
|
82
|
+
# 親インスタンスが感知できないので、いちいちリロードするように変更
|
83
|
+
if Rails::VERSION::MAJOR == 3
|
84
|
+
def add_stock_to_cart_with_reload(*args)
|
85
|
+
add_stock_to_cart_without_reload(*args).tap { @incomplete_order = nil }
|
86
|
+
end
|
87
|
+
alias_method_chain :add_stock_to_cart, :reload
|
88
|
+
|
89
|
+
def reset_stock_from_cart_with_reload(*args)
|
90
|
+
reset_stock_from_cart_without_reload(*args).tap { @incomplete_order = nil }
|
91
|
+
end
|
92
|
+
alias_method_chain :reset_stock_from_cart, :reload
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Comable
|
2
|
+
class Order < ActiveRecord::Base
|
3
|
+
include Decoratable
|
4
|
+
|
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
|
7
|
+
has_many :order_deliveries, dependent: :destroy, class_name: Comable::OrderDelivery.name, foreign_key: table_name.singularize.foreign_key
|
8
|
+
|
9
|
+
accepts_nested_attributes_for :order_deliveries
|
10
|
+
|
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
|
47
|
+
|
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
|
61
|
+
|
62
|
+
private
|
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
|
+
|
78
|
+
def generate_code
|
79
|
+
self.code = loop do
|
80
|
+
random_token = "C#{Array.new(11) { rand(9) }.join}"
|
81
|
+
break random_token unless self.class.exists?(code: random_token)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
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
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Comable
|
2
|
+
class OrderDelivery < ActiveRecord::Base
|
3
|
+
include Decoratable
|
4
|
+
|
5
|
+
belongs_to :order, class_name: Comable::Order.name, foreign_key: Comable::Order.table_name.singularize.foreign_key
|
6
|
+
has_many :order_details, dependent: :destroy, class_name: Comable::OrderDetail.name, foreign_key: table_name.singularize.foreign_key
|
7
|
+
|
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
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Comable
|
2
|
+
class OrderDetail < ActiveRecord::Base
|
3
|
+
include Decoratable
|
4
|
+
|
5
|
+
belongs_to :stock, class_name: Comable::Stock.name, foreign_key: Comable::Stock.table_name.singularize.foreign_key
|
6
|
+
belongs_to :order_delivery, class_name: Comable::OrderDelivery.name, foreign_key: Comable::OrderDelivery.table_name.singularize.foreign_key
|
7
|
+
|
8
|
+
# TODO: バリデーションの追加
|
9
|
+
|
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
|
41
|
+
end
|
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
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Comable
|
2
|
+
class Product < ActiveRecord::Base
|
3
|
+
include Decoratable
|
4
|
+
|
5
|
+
has_many :stocks, class_name: Comable::Stock.name, foreign_key: table_name.singularize.foreign_key
|
6
|
+
after_create :create_stock
|
7
|
+
|
8
|
+
def unsold?
|
9
|
+
stocks.activated.unsold.exists?
|
10
|
+
end
|
11
|
+
|
12
|
+
def soldout?
|
13
|
+
!unsold?
|
14
|
+
end
|
15
|
+
|
16
|
+
def sku_h?
|
17
|
+
sku_h_item_name.present?
|
18
|
+
end
|
19
|
+
|
20
|
+
def sku_v?
|
21
|
+
sku_v_item_name.present?
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :sku?, :sku_h?
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def create_stock
|
29
|
+
stocks.create(code: code) unless stocks.exists?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Comable
|
2
|
+
#
|
3
|
+
# 在庫モデル。
|
4
|
+
# 商品に複数紐付き、品数やSKU(Stock Keeping Unit)情報を保持する。
|
5
|
+
#
|
6
|
+
class Stock < ActiveRecord::Base
|
7
|
+
include Decoratable
|
8
|
+
|
9
|
+
belongs_to :product, class_name: Comable::Product.name, foreign_key: Comable::Product.table_name.singularize.foreign_key
|
10
|
+
|
11
|
+
#
|
12
|
+
# @!group Scope
|
13
|
+
#
|
14
|
+
|
15
|
+
# @!scope class
|
16
|
+
# 有効な在庫インスタンスを返す
|
17
|
+
scope :activated, -> { where.not(product_id_num: nil) }
|
18
|
+
|
19
|
+
# @!scope class
|
20
|
+
# 品切れでない在庫インスタンスを返す
|
21
|
+
scope :unsold, -> { where('quantity > ?', 0) }
|
22
|
+
|
23
|
+
# @!scope class
|
24
|
+
# 品切れの在庫インスタンスを返す
|
25
|
+
scope :soldout, -> { where('quantity <= ?', 0) }
|
26
|
+
|
27
|
+
#
|
28
|
+
# @!endgroup
|
29
|
+
#
|
30
|
+
|
31
|
+
delegate :price, to: :product
|
32
|
+
delegate :sku?, to: :product
|
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?
|
48
|
+
def unsold?
|
49
|
+
return false if product_id_num.nil?
|
50
|
+
return false if quantity.nil?
|
51
|
+
quantity > 0
|
52
|
+
end
|
53
|
+
|
54
|
+
# 在庫の有無を取得する
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
# stock.soldout? #=> false
|
58
|
+
#
|
59
|
+
# @return [Boolean] {#unsold?} の逆。在庫がなければ true を返す
|
60
|
+
# @see #unsold?
|
61
|
+
def soldout?
|
62
|
+
!unsold?
|
63
|
+
end
|
64
|
+
|
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)
|
75
|
+
with_lock do
|
76
|
+
# TODO: カラムマッピングのdecrementメソッドへの対応
|
77
|
+
self.quantity -= quantity
|
78
|
+
save!
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
ja:
|
2
|
+
comable:
|
3
|
+
carts:
|
4
|
+
add_product: '1つの商品がカートに入りしました'
|
5
|
+
product_not_found: 'ご指定の商品は見つかりませんでした'
|
6
|
+
product_not_stocked: 'ご指定の商品は在庫がありません'
|
7
|
+
empty: 'カートに商品が入っていません'
|
8
|
+
update: 'カートの内容が変更されました'
|
9
|
+
orders:
|
10
|
+
success: '注文が完了しました'
|
11
|
+
failure: '注文に失敗しました。入力項目を見直してください。'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateComableProducts < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :comable_products do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.string :code, null: false
|
6
|
+
t.integer :price, null: false
|
7
|
+
t.text :caption
|
8
|
+
t.string :sku_h_item_name
|
9
|
+
t.string :sku_v_item_name
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :comable_products, :code, unique: true, name: :comable_products_idx_01
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateComableStocks < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :comable_stocks do |t|
|
4
|
+
t.integer :comable_product_id
|
5
|
+
t.integer :product_id_num
|
6
|
+
t.string :code, null: false
|
7
|
+
t.integer :quantity
|
8
|
+
t.string :sku_h_choice_name
|
9
|
+
t.string :sku_v_choice_name
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :comable_stocks, :code, unique: true, name: :comable_stocks_idx_01
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateComableOrders < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :comable_orders do |t|
|
4
|
+
t.integer :comable_customer_id
|
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
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index :comable_orders, :code, unique: true, name: :comable_orders_idx_01
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateComableOrderDetails < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :comable_order_details do |t|
|
4
|
+
t.integer :comable_order_delivery_id, null: false
|
5
|
+
t.integer :comable_stock_id, null: false
|
6
|
+
t.integer :price
|
7
|
+
t.integer :quantity, default: 1, null: false
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :comable_order_details, [:comable_order_delivery_id, :comable_stock_id], unique: true, name: :comable_order_details_idx_01
|
11
|
+
end
|
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
|
@@ -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_items.first.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,10 @@
|
|
1
|
+
module Comable
|
2
|
+
module Decoratable
|
3
|
+
def self.included(base)
|
4
|
+
# refs: http://edgeguides.rubyonrails.org/engines.html#overriding-models-and-controllers
|
5
|
+
Dir.glob(Rails.root + "app/decorators/comable/#{base.name.demodulize.underscore}_decorator*.rb").each do |c|
|
6
|
+
require_dependency(c)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
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,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
|
data/lib/comable_core.rb
ADDED
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: comable_core
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- YOSHIDA Hiroki
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.2.0
|
27
|
+
description: Provide core functions for Comable.
|
28
|
+
email:
|
29
|
+
- hyoshida@appirits.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- Rakefile
|
35
|
+
- app/assets/javascripts/comable/application.js
|
36
|
+
- app/assets/stylesheets/comable/application.css
|
37
|
+
- app/helpers/comable/application_helper.rb
|
38
|
+
- app/helpers/comable/products_helper.rb
|
39
|
+
- app/models/comable/customer.rb
|
40
|
+
- app/models/comable/order.rb
|
41
|
+
- app/models/comable/order_delivery.rb
|
42
|
+
- app/models/comable/order_detail.rb
|
43
|
+
- app/models/comable/payment.rb
|
44
|
+
- app/models/comable/product.rb
|
45
|
+
- app/models/comable/stock.rb
|
46
|
+
- config/locales/ja.yml
|
47
|
+
- db/migrate/20131214194807_create_comable_products.rb
|
48
|
+
- db/migrate/20140120032559_create_comable_customers.rb
|
49
|
+
- db/migrate/20140502060116_create_comable_stocks.rb
|
50
|
+
- db/migrate/20140723175431_create_comable_orders.rb
|
51
|
+
- db/migrate/20140723175624_create_comable_order_deliveries.rb
|
52
|
+
- db/migrate/20140723175810_create_comable_order_details.rb
|
53
|
+
- db/migrate/20140817194104_create_comable_payments.rb
|
54
|
+
- lib/comable/cart_owner.rb
|
55
|
+
- lib/comable/core/engine.rb
|
56
|
+
- lib/comable/decoratable.rb
|
57
|
+
- lib/comable/errors.rb
|
58
|
+
- lib/comable/payment_method.rb
|
59
|
+
- lib/comable/payment_method/base.rb
|
60
|
+
- lib/comable/payment_method/general.rb
|
61
|
+
- lib/comable_core.rb
|
62
|
+
- lib/tasks/comable_tasks.rake
|
63
|
+
homepage: https://github.com/hyoshida/comable#comable
|
64
|
+
licenses: []
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.2.2
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: Provide core functions for Comable.
|
86
|
+
test_files: []
|
87
|
+
has_rdoc:
|