comable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +46 -0
- data/Rakefile +28 -0
- data/app/assets/javascripts/comable/application.js +13 -0
- data/app/assets/stylesheets/comable/application.css +13 -0
- data/app/controllers/comable/application_controller.rb +5 -0
- data/app/controllers/comable/carts_controller.rb +29 -0
- data/app/controllers/comable/orders_controller.rb +108 -0
- data/app/controllers/comable/products_controller.rb +11 -0
- data/app/helpers/comable/application_helper.rb +18 -0
- data/app/helpers/comable/products_helper.rb +55 -0
- data/app/models/comable/cart_item.rb +36 -0
- data/app/models/comable/customer.rb +115 -0
- data/app/models/comable/order.rb +29 -0
- data/app/models/comable/order_delivery.rb +12 -0
- data/app/models/comable/order_detail.rb +13 -0
- data/app/models/comable/product.rb +32 -0
- data/app/models/comable/stock.rb +31 -0
- data/app/views/comable/carts/show.slim +23 -0
- data/app/views/comable/orders/confirm.slim +25 -0
- data/app/views/comable/orders/create.slim +5 -0
- data/app/views/comable/orders/delivery.slim +8 -0
- data/app/views/comable/orders/new.slim +4 -0
- data/app/views/comable/orders/orderer.slim +7 -0
- data/app/views/comable/products/index.slim +11 -0
- data/app/views/comable/products/show.slim +20 -0
- data/app/views/layouts/comable/application.slim +11 -0
- data/config/locales/ja.yml +10 -0
- data/config/routes.rb +21 -0
- data/db/migrate/20131214194807_create_comable_products.rb +14 -0
- data/db/migrate/20140120024159_create_comable_cart_items.rb +12 -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 +13 -0
- data/db/migrate/20140723175624_create_comable_order_deliveries.rb +9 -0
- data/db/migrate/20140723175810_create_comable_order_details.rb +10 -0
- data/lib/comable.rb +8 -0
- data/lib/comable/cash_register.rb +90 -0
- data/lib/comable/decoratable.rb +10 -0
- data/lib/comable/engine.rb +8 -0
- data/lib/comable/migration.rb +16 -0
- data/lib/comable/version.rb +3 -0
- data/lib/tasks/comable_tasks.rake +4 -0
- metadata +284 -0
@@ -0,0 +1,29 @@
|
|
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
|
+
has_many :order_deliveries, dependent: :destroy, class_name: Comable::OrderDelivery.name, foreign_key: table_name.singularize.foreign_key
|
7
|
+
|
8
|
+
accepts_nested_attributes_for :order_deliveries
|
9
|
+
|
10
|
+
validates :family_name, presence: true
|
11
|
+
validates :first_name, presence: true
|
12
|
+
|
13
|
+
before_create :generate_code
|
14
|
+
before_create :generate_ordered_at
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def generate_code
|
19
|
+
self.code = loop do
|
20
|
+
random_token = "C#{Array.new(11) { rand(9) }.join}"
|
21
|
+
break random_token unless self.class.exists?(code: random_token)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def generate_ordered_at
|
26
|
+
self.ordered_at = Time.now
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,12 @@
|
|
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
|
+
accepts_nested_attributes_for :order_details
|
9
|
+
|
10
|
+
delegate :customer, to: :order
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
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
|
+
after_create :decrement_quantity!
|
9
|
+
delegate :decrement_quantity!, to: :stock
|
10
|
+
|
11
|
+
delegate :product, to: :stock
|
12
|
+
end
|
13
|
+
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,31 @@
|
|
1
|
+
module Comable
|
2
|
+
class Stock < ActiveRecord::Base
|
3
|
+
include Decoratable
|
4
|
+
|
5
|
+
belongs_to :product, class_name: Comable::Product.name, foreign_key: Comable::Product.table_name.singularize.foreign_key
|
6
|
+
|
7
|
+
scope :activated, -> { where.not(product_id_num: nil) }
|
8
|
+
scope :unsold, -> { where('quantity > ?', 0) }
|
9
|
+
scope :soldout, -> { where('quantity <= ?', 0) }
|
10
|
+
|
11
|
+
delegate :price, to: :product
|
12
|
+
delegate :sku?, to: :product
|
13
|
+
|
14
|
+
def unsold?
|
15
|
+
return false if product_id_num.nil?
|
16
|
+
return false if quantity.nil?
|
17
|
+
quantity > 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def soldout?
|
21
|
+
!unsold?
|
22
|
+
end
|
23
|
+
|
24
|
+
def decrement_quantity!
|
25
|
+
with_lock do
|
26
|
+
# TODO: カラムマッピングのdecrementメソッドへの対応
|
27
|
+
update_attributes!(quantity: quantity.pred)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
h1 カート
|
2
|
+
|
3
|
+
ul.cart
|
4
|
+
- current_customer.cart.each do |cart_item|
|
5
|
+
- stock = cart_item.stock
|
6
|
+
- product = stock.product
|
7
|
+
li.product
|
8
|
+
h2.name
|
9
|
+
- if stock.sku?
|
10
|
+
- sku_name = stock.sku_h_choice_name
|
11
|
+
- sku_name += '/' + stock.sku_v_choice_name if stock.sku_v_choice_name.present?
|
12
|
+
= link_to product.name + "(#{sku_name})", comable.product_path(product)
|
13
|
+
- else
|
14
|
+
= link_to product.name, comable.product_path(product)
|
15
|
+
.caption
|
16
|
+
= product.caption
|
17
|
+
.price
|
18
|
+
= number_to_currency product.price
|
19
|
+
.quantity
|
20
|
+
= number_with_delimiter cart_item.quantity
|
21
|
+
|
22
|
+
.order
|
23
|
+
= link_to '注文', comable.new_order_path
|
@@ -0,0 +1,25 @@
|
|
1
|
+
h1 注文情報確認
|
2
|
+
|
3
|
+
.order
|
4
|
+
.orderer
|
5
|
+
.family_name
|
6
|
+
= @order.family_name
|
7
|
+
.first_name
|
8
|
+
= @order.first_name
|
9
|
+
.deliveries
|
10
|
+
- @order.order_deliveries.each do |order_delivery|
|
11
|
+
.first_name
|
12
|
+
= order_delivery.family_name
|
13
|
+
.first_name
|
14
|
+
= order_delivery.first_name
|
15
|
+
.details
|
16
|
+
- order_delivery.order_details.each do |order_detail|
|
17
|
+
.name
|
18
|
+
= order_detail.product.name
|
19
|
+
.quantity
|
20
|
+
= order_detail.price
|
21
|
+
.quantity
|
22
|
+
= order_detail.quantity
|
23
|
+
|
24
|
+
= form_for @order, as: :order, url: comable.order_path do |f|
|
25
|
+
= f.submit
|
@@ -0,0 +1,8 @@
|
|
1
|
+
h1 配送先情報入力
|
2
|
+
|
3
|
+
.orderer
|
4
|
+
= form_for @order, as: :order, url: comable.delivery_order_path do |f|
|
5
|
+
= f.fields_for :order_deliveries do |order_delivery|
|
6
|
+
= order_delivery.text_field :family_name, placeholder: '姓'
|
7
|
+
= order_delivery.text_field :first_name, placeholder: '名'
|
8
|
+
= f.submit
|
@@ -0,0 +1,20 @@
|
|
1
|
+
.product
|
2
|
+
h1.name
|
3
|
+
= @product.name
|
4
|
+
.caption
|
5
|
+
= @product.caption
|
6
|
+
.price
|
7
|
+
= number_to_currency @product.price
|
8
|
+
|
9
|
+
= form_tag comable.add_cart_path do
|
10
|
+
- if @product.sku?
|
11
|
+
.sku
|
12
|
+
= sku_table @product, border: 1
|
13
|
+
|
14
|
+
- if @product.unsold?
|
15
|
+
.add_cart
|
16
|
+
= hidden_field_tag :product_id, @product.id
|
17
|
+
= submit_tag 'カートに入れる'
|
18
|
+
- else
|
19
|
+
.sold_out
|
20
|
+
| 品切れ中
|
@@ -0,0 +1,11 @@
|
|
1
|
+
doctype html
|
2
|
+
html
|
3
|
+
head
|
4
|
+
title Comable
|
5
|
+
= stylesheet_link_tag "comable/application", media: "all"
|
6
|
+
= javascript_include_tag "comable/application"
|
7
|
+
= csrf_meta_tags
|
8
|
+
body
|
9
|
+
- flash.each do |name, msg|
|
10
|
+
= content_tag(:div, msg, id: "flash_#{name}")
|
11
|
+
== yield
|
data/config/routes.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Comable::Engine.routes.draw do
|
2
|
+
get '/' => 'products#index'
|
3
|
+
|
4
|
+
resources :products
|
5
|
+
|
6
|
+
resource :cart do
|
7
|
+
collection do
|
8
|
+
post :add
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
resource :order do
|
13
|
+
collection do
|
14
|
+
get :orderer
|
15
|
+
post :orderer
|
16
|
+
get :delivery
|
17
|
+
post :delivery
|
18
|
+
get :confirm
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateComableProducts < Comable::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,12 @@
|
|
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
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateComableStocks < Comable::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,13 @@
|
|
1
|
+
class CreateComableOrders < Comable::Migration
|
2
|
+
def change
|
3
|
+
create_table :comable_orders do |t|
|
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
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :comable_orders, :code, unique: true, name: :comable_orders_idx_01
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class CreateComableOrderDetails < Comable::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, null: false
|
7
|
+
t.integer :quantity, default: 1, null: false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/lib/comable.rb
ADDED
@@ -0,0 +1,90 @@
|
|
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
|