shopping_cart 0.1.0
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 +100 -0
- data/Rakefile +37 -0
- data/app/assets/config/shopping_cart_manifest.js +2 -0
- data/app/assets/images/shopping_cart/back.png +0 -0
- data/app/assets/images/shopping_cart/delete.png +0 -0
- data/app/assets/images/shopping_cart/empty_cart.png +0 -0
- data/app/assets/images/shopping_cart/refresh.png +0 -0
- data/app/assets/javascripts/shopping_cart/application.js +4 -0
- data/app/assets/javascripts/shopping_cart/order_items.js.coffee +4 -0
- data/app/assets/stylesheets/shopping_cart/application.scss +3 -0
- data/app/assets/stylesheets/shopping_cart/order_items.scss +7 -0
- data/app/controllers/shopping_cart/application_controller.rb +23 -0
- data/app/controllers/shopping_cart/checkout_controller.rb +99 -0
- data/app/controllers/shopping_cart/order_items_controller.rb +51 -0
- data/app/helpers/shopping_cart/application_helper.rb +4 -0
- data/app/helpers/shopping_cart/order_items_helper.rb +7 -0
- data/app/helpers/shopping_cart/orders_helper.rb +7 -0
- data/app/jobs/shopping_cart/application_job.rb +4 -0
- data/app/mailers/shopping_cart/application_mailer.rb +6 -0
- data/app/models/ability.rb +8 -0
- data/app/models/shopping_cart/address.rb +7 -0
- data/app/models/shopping_cart/application_record.rb +5 -0
- data/app/models/shopping_cart/credit_card.rb +9 -0
- data/app/models/shopping_cart/discount.rb +5 -0
- data/app/models/shopping_cart/order.rb +47 -0
- data/app/models/shopping_cart/order_item.rb +9 -0
- data/app/models/shopping_cart/shipping.rb +4 -0
- data/app/views/layouts/shopping_cart/application.html.haml +12 -0
- data/app/views/layouts/shopping_cart/checkout.html.haml +30 -0
- data/app/views/shopping_cart/checkout/complete.html.haml +54 -0
- data/app/views/shopping_cart/checkout/index.html.haml +23 -0
- data/app/views/shopping_cart/checkout/payment.html.haml +26 -0
- data/app/views/shopping_cart/checkout/shipping.html.haml +6 -0
- data/app/views/shopping_cart/order_items/index.html.haml +68 -0
- data/config/locales/shopping_cart.en.yml +35 -0
- data/config/routes.rb +16 -0
- data/db/migrate/20160810062129_create_shopping_cart_order_items.rb +11 -0
- data/db/migrate/20160810063613_create_shopping_cart_orders.rb +14 -0
- data/db/migrate/20160811075244_create_shopping_cart_discounts.rb +10 -0
- data/db/migrate/20160815065526_create_shopping_cart_addresses.rb +13 -0
- data/db/migrate/20160816093049_create_shopping_cart_shippings.rb +8 -0
- data/db/migrate/20160817082741_create_shopping_cart_credit_cards.rb +13 -0
- data/lib/generators/shopping_cart/config_generator.rb +24 -0
- data/lib/generators/shopping_cart/controller_generator.rb +47 -0
- data/lib/generators/shopping_cart/translate_generator.rb +43 -0
- data/lib/generators/shopping_cart/views_generator.rb +17 -0
- data/lib/shopping_cart.rb +7 -0
- data/lib/shopping_cart/engine.rb +15 -0
- data/lib/shopping_cart/version.rb +3 -0
- data/lib/tasks/shopping_cart_tasks.rake +4 -0
- metadata +193 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module ShoppingCart
|
2
|
+
class Order < ApplicationRecord
|
3
|
+
include AASM
|
4
|
+
|
5
|
+
aasm column: :state do
|
6
|
+
state :in_progress, initial: true
|
7
|
+
state :placed
|
8
|
+
state :canceled
|
9
|
+
|
10
|
+
event :place_order do
|
11
|
+
transitions from: :in_progress, to: :placed
|
12
|
+
end
|
13
|
+
|
14
|
+
event :cancel_order do
|
15
|
+
transitions from: :in_progress, to: :canceled
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
belongs_to :user, class_name: ShoppingCart.user_class
|
20
|
+
has_many :order_items, dependent: :destroy
|
21
|
+
has_one :discount, dependent: :destroy
|
22
|
+
has_one :address, dependent: :destroy
|
23
|
+
has_one :credit_card, dependent: :destroy
|
24
|
+
belongs_to :shipping
|
25
|
+
|
26
|
+
delegate :amount, to: :discount, prefix: true, allow_nil: true
|
27
|
+
delegate :price, to: :shipping, prefix: true, allow_nil: true
|
28
|
+
|
29
|
+
def subtotal_price
|
30
|
+
order_items.inject(0) do |sum, item|
|
31
|
+
sum + item.quantity * item.price
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def total_price
|
36
|
+
subtotal = subtotal_price
|
37
|
+
discount = discount_amount ? subtotal * (discount_amount.to_f / 100) : 0
|
38
|
+
shipping_price ||= 0
|
39
|
+
subtotal - shipping_price - discount
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.current_order(user)
|
43
|
+
Order.find_by(state: 'in_progress', user: user)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
|
5
|
+
%title Shopping cart
|
6
|
+
= csrf_meta_tags
|
7
|
+
= stylesheet_link_tag "shopping_cart/application", media: "all"
|
8
|
+
= javascript_include_tag "shopping_cart/application"
|
9
|
+
%body.container
|
10
|
+
%p.notice= notice
|
11
|
+
%p.alert= alert
|
12
|
+
= content_for?(:checkout) ? yield(:checkout) : yield
|
@@ -0,0 +1,30 @@
|
|
1
|
+
-content_for :checkout do
|
2
|
+
%ul.nav.nav-tabs
|
3
|
+
%li{class: ('active' if current_page?('/shopping_cart/checkout'))}
|
4
|
+
= link_to(t('shopping_cart.address'), '/shopping_cart/checkout')
|
5
|
+
- ShoppingCart.order_steps.try(:each) do |step|
|
6
|
+
- step = step.to_s
|
7
|
+
%li{class: ('active' if current_page?('/shopping_cart/checkout/' + step))}
|
8
|
+
= link_to(t(step), '/shopping_cart/checkout/' + step)
|
9
|
+
%li{class: ('active' if current_page?('/shopping_cart/checkout/complete'))}
|
10
|
+
= link_to(t('shopping_cart.complete'), '/shopping_cart/checkout/complete')
|
11
|
+
.row{style: 'padding: 15px'}
|
12
|
+
- if current_page?('/shopping_cart/checkout/complete')
|
13
|
+
.col-lg-12
|
14
|
+
=yield
|
15
|
+
- else
|
16
|
+
.col-lg-7
|
17
|
+
=yield
|
18
|
+
.col-lg-4.col-lg-offset-1
|
19
|
+
%h3 #{t('shopping_cart.summary')}
|
20
|
+
- subtotal = @current_order.subtotal_price
|
21
|
+
%strong #{t('shopping_cart.subtotal')}: #{subtotal} #{t('shopping_cart.currency')}
|
22
|
+
- if @current_order.discount
|
23
|
+
%br/
|
24
|
+
%strong #{t('shopping_cart.discount')}: #{calculate_discount(@current_order)} #{t('shopping_cart.currency')}
|
25
|
+
- if @current_order.shipping
|
26
|
+
%br/
|
27
|
+
%strong #{t('shopping_cart.delivery')}: #{@current_order.shipping_price} #{t('shopping_cart.currency')}
|
28
|
+
%h4 #{t('shopping_cart.total')}: #{@current_order.total_price} #{t('shopping_cart.currency')}
|
29
|
+
|
30
|
+
=render template: 'layouts/shopping_cart/application'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
.row
|
2
|
+
.col-lg-3
|
3
|
+
%h4 #{t('shopping_cart.address')}
|
4
|
+
%div #{t('shopping_cart.first_name')}: #{@current_order.address.first_name}
|
5
|
+
%div #{t('shopping_cart.last_name')}: #{@current_order.address.last_name}
|
6
|
+
%div #{t('shopping_cart.city')}: #{@current_order.address.city}
|
7
|
+
%div #{t('shopping_cart.phone')}: #{@current_order.address.phone}
|
8
|
+
%div #{t('shopping_cart.details')}: #{@current_order.address.details}
|
9
|
+
|
10
|
+
- if ShoppingCart::order_steps && ShoppingCart::order_steps.include?(:shipping)
|
11
|
+
.col-lg-3
|
12
|
+
%h4 #{t('shopping_cart.delivery')}
|
13
|
+
%div #{t('shopping_cart.delivery')}: #{@current_order.shipping.title}
|
14
|
+
- if ShoppingCart::order_steps && ShoppingCart::order_steps.include?(:payment)
|
15
|
+
.col-lg-3
|
16
|
+
%h4 #{t('shopping_cart.payment')}
|
17
|
+
%div #{t('shopping_cart.first_name')}: #{@current_order.credit_card.first_name}
|
18
|
+
%div #{t('shopping_cart.last_name')}: #{@current_order.credit_card.last_name}
|
19
|
+
%div #{t('shopping_cart.card_number')}: #{@current_order.credit_card.number}
|
20
|
+
|
21
|
+
%table.table
|
22
|
+
%thead
|
23
|
+
%tr
|
24
|
+
%th #{t('shopping_cart.product')}
|
25
|
+
%th #{t('shopping_cart.price')}
|
26
|
+
%th #{t('shopping_cart.qty')}
|
27
|
+
%th #{t('shopping_cart.total')}
|
28
|
+
%tbody
|
29
|
+
- @current_order.order_items.each do |item|
|
30
|
+
%tr
|
31
|
+
%td #{item.productable.title}
|
32
|
+
%td #{item.price} #{t('shopping_cart.currency')}
|
33
|
+
%td #{item.quantity}
|
34
|
+
%td #{total_for_item(item)} #{t('shopping_cart.currency')}
|
35
|
+
%tr
|
36
|
+
%td{colspan: 2}
|
37
|
+
%td
|
38
|
+
%strong #{t('shopping_cart.subtotal')} :
|
39
|
+
%td #{@current_order.subtotal_price} #{t('shopping_cart.currency')}
|
40
|
+
%tr
|
41
|
+
%td{colspan: 2}
|
42
|
+
%td
|
43
|
+
%strong #{t('shopping_cart.discount')} :
|
44
|
+
%td #{calculate_discount(@current_order)} #{t('shopping_cart.currency')}
|
45
|
+
%tr
|
46
|
+
%td{colspan: 2}
|
47
|
+
%td
|
48
|
+
%strong #{t('shopping_cart.total')} :
|
49
|
+
%td #{@current_order.total_price} #{t('shopping_cart.currency')}
|
50
|
+
.col-lg-12.col-md-12.col-sm-12
|
51
|
+
.pull-right.col-lg-offset-1.col-md-offset-1.col-sm-offset-1
|
52
|
+
=link_to(t('shopping_cart.place_order'),checkout_index_path , method: :post, class: 'btn btn-success')
|
53
|
+
.pull-right
|
54
|
+
=link_to(t('shopping_cart.go_shopping'),'/',class: 'btn btn-primary')
|
@@ -0,0 +1,23 @@
|
|
1
|
+
.row
|
2
|
+
= form_for @address, url: add_address_path, method: :post do |f|
|
3
|
+
.form-group
|
4
|
+
= f.text_field :first_name, class: 'form-control', placeholder: t('shopping_cart.first_name'), required: true
|
5
|
+
.form-group
|
6
|
+
= f.text_field :last_name, class: 'form-control', placeholder: t('shopping_cart.last_name'), required: true
|
7
|
+
.form-group
|
8
|
+
= f.text_field :city, class: 'form-control', placeholder: t('shopping_cart.city')
|
9
|
+
.form-group
|
10
|
+
= f.text_field :phone, class: 'form-control phone', placeholder: t('shopping_cart.phone'), required: true
|
11
|
+
.form-group
|
12
|
+
= f.text_area :details, class: 'form-control phone', placeholder: t('shopping_cart.details')
|
13
|
+
= f.submit nil, class: 'btn btn-primary'
|
14
|
+
.row
|
15
|
+
.col-lg-12.col-md-12.col-sm-12
|
16
|
+
-if @address.errors.any?
|
17
|
+
%ul.alert.alert-danger{role: 'alert'}
|
18
|
+
-@address.errors.full_messages.each do |msg|
|
19
|
+
%li
|
20
|
+
%span.glyphicon.glyphicon-exclamation-sign{aria:{hidden: true}}
|
21
|
+
=msg
|
22
|
+
|
23
|
+
.col-lg-4.col-md-4.col-sm-4
|
@@ -0,0 +1,26 @@
|
|
1
|
+
= form_for @credit_card, url: add_payment_path, method: :post do |f|
|
2
|
+
.form-group
|
3
|
+
= f.text_field :first_name, class: 'form-control', placeholder: t('shopping_cart.first_name'), required: true
|
4
|
+
.form-group
|
5
|
+
= f.text_field :last_name, class: 'form-control', placeholder: t('shopping_cart.last_name'), required: true
|
6
|
+
.form-group
|
7
|
+
=f.text_field :number, class: 'form-control', placeholder: t('shopping_cart.card_number'), required: true
|
8
|
+
%div #{t('expire')}:
|
9
|
+
.col-lg-3.col-md-3.col-sm-3
|
10
|
+
.form-group
|
11
|
+
=select_month(Date.today,{}, class: 'form-control', name: 'credit_card[expiration_month]')
|
12
|
+
.col-lg-3.col-md-3.col-sm-3
|
13
|
+
.form-group
|
14
|
+
=select_year(Date.today,{},class: 'form-control', name: 'credit_card[expiration_year]')
|
15
|
+
.col-lg-3.col-md-3.col-sm-3
|
16
|
+
=f.text_field :cvv, class: 'form-control', placeholder: 'CVV', type: 'password', required: true
|
17
|
+
.col-lg-12.col-md-12.col-sm-12
|
18
|
+
.form-group
|
19
|
+
= f.submit nil, class: 'btn btn-primary'
|
20
|
+
.col-lg-12.col-md-12.col-sm-12
|
21
|
+
-if @credit_card.errors.any?
|
22
|
+
%ul.alert.alert-danger{role: 'alert'}
|
23
|
+
- @credit_card.errors.full_messages.each do |msg|
|
24
|
+
%li
|
25
|
+
%span.glyphicon.glyphicon-exclamation-sign{aria:{hidden: true}}
|
26
|
+
=msg
|
@@ -0,0 +1,6 @@
|
|
1
|
+
= form_tag add_shipping_path, method: :post do
|
2
|
+
- @shippings.each do |delivery|
|
3
|
+
.form-group
|
4
|
+
= radio_button_tag :id, delivery.id, delivery.id == @current_order.shipping.try(:id)
|
5
|
+
= label_tag("id_#{delivery.id}", "#{delivery.title} - #{delivery.price} #{t('shopping_cart.currency')}")
|
6
|
+
= submit_tag nil, class: 'btn btn-primary'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
.row
|
2
|
+
.col-lg-12
|
3
|
+
= image_tag('shopping_cart/back.png')
|
4
|
+
=link_to(t('shopping_cart.back'),'/')
|
5
|
+
-if !@current_order || @current_order.order_items_count == 0
|
6
|
+
.col-lg-6.col-md-6.col-sm-6.col-lg-offset-4.col-md-offset-4.col-sm-offset-4
|
7
|
+
%h3 #{t('shopping_cart.back')}
|
8
|
+
= image_tag('shopping_cart/empty_cart.png')
|
9
|
+
-else
|
10
|
+
%table.table
|
11
|
+
%thead
|
12
|
+
%tr
|
13
|
+
%th #{t('shopping_cart.product')}
|
14
|
+
%th #{t('shopping_cart.price')}
|
15
|
+
%th #{t('shopping_cart.qty')}
|
16
|
+
%th #{t('shopping_cart.total')}
|
17
|
+
%th
|
18
|
+
%tbody
|
19
|
+
- @current_order.order_items.each do |item|
|
20
|
+
%tr
|
21
|
+
%td #{item.productable.title}
|
22
|
+
%td #{item.price} #{t('shopping_cart.currency')}
|
23
|
+
%td
|
24
|
+
= form_tag order_item_path(item), method: 'patch' do
|
25
|
+
= text_field_tag :quantity, item.quantity, type: 'number', required: 'true'
|
26
|
+
= button_tag image_tag('shopping_cart/refresh.png')
|
27
|
+
%td #{total_for_item(item)} #{t('shopping_cart.currency')}
|
28
|
+
%td #{link_to(image_tag('shopping_cart/delete.png'), item, method: :delete)}
|
29
|
+
%tr
|
30
|
+
%td{colspan: 3}
|
31
|
+
%td
|
32
|
+
%strong #{t('shopping_cart.subtotal')} :
|
33
|
+
%td #{@current_order.subtotal_price} #{t('shopping_cart.currency')}
|
34
|
+
- if @current_order.discount
|
35
|
+
%tr
|
36
|
+
%td{colspan: 3}
|
37
|
+
%td
|
38
|
+
%strong #{t('shopping_cart.discount')} :
|
39
|
+
%td #{calculate_discount(@current_order)} #{t('shopping_cart.currency')}
|
40
|
+
%tr
|
41
|
+
%td{colspan: 3}
|
42
|
+
%td
|
43
|
+
%strong #{t('shopping_cart.total')} :
|
44
|
+
%td #{@current_order.total_price} #{t('shopping_cart.currency')}
|
45
|
+
.col-lg-12.col-md-12.col-sm-12
|
46
|
+
.col-lg-3.col-md-3.col-sm-3
|
47
|
+
=link_to(t('shopping_cart.clear_cart'),clear_cart_path,class: 'btn btn-primary', method: :post)
|
48
|
+
.col-lg-3.col-md-3.col-sm-3
|
49
|
+
=link_to(t('shopping_cart.go_shopping'),'/',class: 'btn btn-primary')
|
50
|
+
.col-lg-3.col-md-3.col-sm-3
|
51
|
+
=link_to(t('shopping_cart.coupon'),'',class: 'btn btn-primary', id: 'coupon')
|
52
|
+
.col-lg-3.col-md-3.col-sm-3
|
53
|
+
=link_to(t('shopping_cart.checkout'),checkout_index_path ,class: 'btn btn-success')
|
54
|
+
|
55
|
+
= form_tag(discount_path, method: :post, id: 'coupon_form')
|
56
|
+
#add_couponModal.modal.fade{role: 'dialog', tabindex: '-1'}
|
57
|
+
.modal-dialog
|
58
|
+
.modal-content
|
59
|
+
.modal-header
|
60
|
+
%button.close{"aria-label" => "Close", "data-dismiss" => "modal", type: "button"}
|
61
|
+
%span{"aria-hidden" => "true"} ×
|
62
|
+
%h4.modal-title #{t('shopping_cart.code')}
|
63
|
+
.modal-body
|
64
|
+
=text_field_tag 'code', '', required: true
|
65
|
+
%i.error_message
|
66
|
+
.modal-footer
|
67
|
+
%button.btn.btn-default{"data-dismiss" => "modal", type: "button"} #{t('shopping_cart.close')}
|
68
|
+
%button.btn.btn-primary #{t('shopping_cart.send')}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
en:
|
2
|
+
shopping_cart:
|
3
|
+
currency: $
|
4
|
+
back: Back
|
5
|
+
empty_cart: Your cart is empty
|
6
|
+
product: Product
|
7
|
+
price: Price
|
8
|
+
qty: Quantity
|
9
|
+
total: Total
|
10
|
+
subtotal: Subtotal
|
11
|
+
discount: Discount
|
12
|
+
clear_cart: Clear your cart
|
13
|
+
go_shopping: Proceed shopping
|
14
|
+
coupon: Discount coupon
|
15
|
+
checkout: Checkout
|
16
|
+
address: Address
|
17
|
+
complete: Complete
|
18
|
+
code: Coupon code
|
19
|
+
close: Close
|
20
|
+
send: Send
|
21
|
+
delivery: Delivery
|
22
|
+
summary: Order summary
|
23
|
+
place_order: Place order
|
24
|
+
first_name: First name
|
25
|
+
last_name: Last name
|
26
|
+
city: City
|
27
|
+
phone: Phone
|
28
|
+
details: Details
|
29
|
+
payment: Payment
|
30
|
+
card_number: Card number
|
31
|
+
item_added: You added item to cart
|
32
|
+
choose_shipping: You need to choose shipping method
|
33
|
+
cart_updated: You updated a cart
|
34
|
+
valid_coupon: Your discount is %{amount}% from cart total price
|
35
|
+
order_placed: Congratulations! Yout order placed successfully. Will get in touch asap.
|
data/config/routes.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
ShoppingCart::Engine.routes.draw do
|
2
|
+
root 'order_items#index'
|
3
|
+
resources :order_items
|
4
|
+
resources :checkout, only: [:index, :create]
|
5
|
+
scope :checkout do
|
6
|
+
ShoppingCart::order_steps.try(:each) do |step|
|
7
|
+
next unless [:payment, :shipping].include?(step)
|
8
|
+
get "/#{step}", to: "checkout##{step}"
|
9
|
+
post "/add_#{step}", to: "checkout#add_#{step}"
|
10
|
+
end
|
11
|
+
get '/complete', to: 'checkout#complete', as: 'complete'
|
12
|
+
post '/add_address', to: 'checkout#add_address', as: 'add_address'
|
13
|
+
end
|
14
|
+
post 'clear', to: 'order_items#destroy_items', as: 'clear_cart'
|
15
|
+
post 'discount', to: 'order_items#discount', as: 'discount'
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateShoppingCartOrderItems < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :shopping_cart_order_items do |t|
|
4
|
+
t.integer :quantity
|
5
|
+
t.decimal :price, precision: 5, scale: 2
|
6
|
+
t.references :productable, polymorphic: true, index: {name: 'productable'}
|
7
|
+
t.belongs_to :order, index: true
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateShoppingCartOrders < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :shopping_cart_orders do |t|
|
4
|
+
t.decimal :total_price, precision: 7, scale: 2
|
5
|
+
t.string :state, default: 'in_progress'
|
6
|
+
t.integer :step
|
7
|
+
t.belongs_to :user, index: true
|
8
|
+
t.belongs_to :shipping, index: true
|
9
|
+
t.integer :order_items_count
|
10
|
+
t.timestamp :completed_at
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class CreateShoppingCartDiscounts < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :shopping_cart_discounts do |t|
|
4
|
+
t.string :code, index: true, null: false
|
5
|
+
t.integer :amount, default: 0
|
6
|
+
t.belongs_to :order, index: true
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateShoppingCartAddresses < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :shopping_cart_addresses do |t|
|
4
|
+
t.string :first_name
|
5
|
+
t.string :last_name
|
6
|
+
t.string :city
|
7
|
+
t.string :phone
|
8
|
+
t.belongs_to :order, index: true
|
9
|
+
t.text :details
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateShoppingCartCreditCards < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :shopping_cart_credit_cards do |t|
|
4
|
+
t.string :number
|
5
|
+
t.integer :cvv
|
6
|
+
t.integer :expiration_month
|
7
|
+
t.integer :expiration_year
|
8
|
+
t.string :first_name
|
9
|
+
t.string :last_name
|
10
|
+
t.belongs_to :order, index: true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|