locomotive_ecommerce_plugin 1.0.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 +15 -0
- data/Gemfile +40 -0
- data/app/assets/javascripts/locomotive/ecommerce/application.js +15 -0
- data/app/assets/stylesheets/locomotive/ecommerce/application.css +13 -0
- data/app/assets/stylesheets/locomotive/ecommerce/flash_dance.alerts.bootstrap.css +107 -0
- data/app/controllers/locomotive/ecommerce/application_controller.rb +35 -0
- data/app/controllers/locomotive/ecommerce/cart_controller.rb +12 -0
- data/app/controllers/locomotive/ecommerce/order_controller.rb +18 -0
- data/app/controllers/locomotive/ecommerce/purchase_controller.rb +56 -0
- data/app/helpers/locomotive/ecommerce/ecommerce_cart_helper.rb +11 -0
- data/app/helpers/locomotive/ecommerce/ecommerce_helper.rb +25 -0
- data/app/helpers/locomotive/ecommerce/ecommerce_url_helper.rb +52 -0
- data/app/mailers/locomotive/ecommerce/purchase_mailer.rb +19 -0
- data/app/models/locomotive/ecommerce/cart.rb +162 -0
- data/app/models/locomotive/ecommerce/order.rb +100 -0
- data/app/models/locomotive/ecommerce/purchase.rb +148 -0
- data/app/views/flash_dance/_alert.html.erb +4 -0
- data/app/views/flash_dance/_error.html.erb +4 -0
- data/app/views/flash_dance/_info.html.erb +4 -0
- data/app/views/flash_dance/_notice.html.erb +4 -0
- data/app/views/flash_dance/_success.html.erb +4 -0
- data/app/views/flash_dance/_warning.html.erb +4 -0
- data/app/views/kaminari/_first_page.html.erb +11 -0
- data/app/views/kaminari/_gap.html.erb +8 -0
- data/app/views/kaminari/_last_page.html.erb +11 -0
- data/app/views/kaminari/_next_page.html.erb +11 -0
- data/app/views/kaminari/_page.html.erb +12 -0
- data/app/views/kaminari/_paginator.html.erb +23 -0
- data/app/views/kaminari/_prev_page.html.erb +11 -0
- data/app/views/locomotive/ecommerce/purchase_mailer/purchase_confirmation.text.erb +11 -0
- data/config/initializers/active_resource.rb +1 -0
- data/config/initializers/kaminari_config.rb +16 -0
- data/config/initializers/liquid_stack_trace.rb +7 -0
- data/config/initializers/stripe_setup.rb +40 -0
- data/config/routes.rb +14 -0
- data/lib/locomotive/ecommerce/plugin.rb +74 -0
- data/lib/locomotive/ecommerce/plugin/config.html +111 -0
- data/lib/locomotive/ecommerce/plugin/ecommerce_drop.rb +106 -0
- data/lib/locomotive/ecommerce/plugin/ecommerce_filters.rb +35 -0
- data/lib/locomotive/ecommerce/plugin/ecommerce_tags.rb +31 -0
- data/lib/locomotive/ecommerce/plugin/engine.rb +42 -0
- data/lib/locomotive/ecommerce/plugin/inventory_interface.rb +15 -0
- data/lib/locomotive/ecommerce/plugin/version.rb +5 -0
- data/lib/locomotive_ecommerce_plugin.rb +1 -0
- metadata +184 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
!binary "U0hBMQ==":
|
|
3
|
+
metadata.gz: !binary |-
|
|
4
|
+
YWE5M2U2MDk3N2QxMGEwZTJjZTlkMTdiYTkyYTY1MGZiYzJhNWYwNg==
|
|
5
|
+
data.tar.gz: !binary |-
|
|
6
|
+
YmY0ZDQ1YzA2Y2E5NDU3MzhhNzBlNzliOThjNWQ5NzliN2VmMTRlMg==
|
|
7
|
+
SHA512:
|
|
8
|
+
metadata.gz: !binary |-
|
|
9
|
+
YTRmZTY0YTM1YjZhYjVkMTQ3Mzc0OGNlN2ZhOWZhNGI0NmY1NGZjOWY2YWYz
|
|
10
|
+
OThlNTY3ZTI3ZDgyZmE4Y2Q4NzkxNTdjYjVkZDA5NDBlMTg5ZWY0ZDgwM2E0
|
|
11
|
+
OTkxMGE1MTM4YzQ1YTlhYTJkNTM4MWMxMjIwM2U1MmRiZTNjOWY=
|
|
12
|
+
data.tar.gz: !binary |-
|
|
13
|
+
ZjlkNDAwMTdkYTdkYWU0NTdmZjZlOWQ4OWJhYjM0MjEwOWRmNDJmNzc0NWQ0
|
|
14
|
+
OTFjMzg1YTNkZDBjNjMxNjNkOTdlMjc2NzlkZGUzNWJiMTJiMTgzMTBlMjdl
|
|
15
|
+
OWI3OWU1M2YxZWM4ZmJkNWYxYzc0OTJlYTkxNzJiMGVkOWZmMDI=
|
data/Gemfile
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
source "http://rubygems.org"
|
|
2
|
+
|
|
3
|
+
# Declare your gem's dependencies in hbird_ecommerce.gemspec.
|
|
4
|
+
# Bundler will treat runtime dependencies like base dependencies, and
|
|
5
|
+
# development dependencies will be added by default to the :development group.
|
|
6
|
+
gemspec
|
|
7
|
+
|
|
8
|
+
# Stripe helper
|
|
9
|
+
gem 'stripe_helper', path: '../../stripe_helper'
|
|
10
|
+
|
|
11
|
+
# jquery-rails is used by the dummy application
|
|
12
|
+
group :test do
|
|
13
|
+
gem 'rspec-rails'
|
|
14
|
+
gem 'capybara'
|
|
15
|
+
gem "factory_girl"
|
|
16
|
+
gem "mocha"
|
|
17
|
+
gem "database_cleaner"
|
|
18
|
+
gem "factory_girl_rails"
|
|
19
|
+
gem "simplecov", require: false
|
|
20
|
+
gem 'shoulda-matchers', require: false
|
|
21
|
+
gem "debugger"
|
|
22
|
+
gem "poltergeist"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# gem "locomotive_cms", path: '../../locomotive_engine', require: 'locomotive/engine'
|
|
26
|
+
# gem "locomotive_plugins", path: '../../locomotive_plugins'
|
|
27
|
+
|
|
28
|
+
group :assets do
|
|
29
|
+
gem 'compass-rails', '~> 1.1.7'
|
|
30
|
+
gem 'sass-rails', '~> 3.2.4'
|
|
31
|
+
gem 'coffee-rails', '~> 3.2.2'
|
|
32
|
+
gem 'uglifier', '~> 1.2.4'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
group :locomotive_plugins do
|
|
36
|
+
gem "locomotive_ecommerce_plugin", path: '.'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# TEMP
|
|
40
|
+
gem 'flash-dance', :git => 'https://github.com/MunkiPhD/flash-dance.git' # It may be not being used.
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
// the compiled file.
|
|
9
|
+
//
|
|
10
|
+
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
|
11
|
+
// GO AFTER THE REQUIRES BELOW.
|
|
12
|
+
//
|
|
13
|
+
//= require jquery
|
|
14
|
+
//= require jquery_ujs
|
|
15
|
+
//= 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,107 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Bootstrap v2.2.1
|
|
3
|
+
*
|
|
4
|
+
* Copyright 2012 Twitter, Inc
|
|
5
|
+
* Licensed under the Apache License v2.0
|
|
6
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
*
|
|
8
|
+
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
|
9
|
+
*/
|
|
10
|
+
.clearfix {
|
|
11
|
+
*zoom: 1;
|
|
12
|
+
}
|
|
13
|
+
.clearfix:before,
|
|
14
|
+
.clearfix:after {
|
|
15
|
+
display: table;
|
|
16
|
+
content: "";
|
|
17
|
+
line-height: 0;
|
|
18
|
+
}
|
|
19
|
+
.clearfix:after {
|
|
20
|
+
clear: both;
|
|
21
|
+
}
|
|
22
|
+
.hide-text {
|
|
23
|
+
font: 0/0 a;
|
|
24
|
+
color: transparent;
|
|
25
|
+
text-shadow: none;
|
|
26
|
+
background-color: transparent;
|
|
27
|
+
border: 0;
|
|
28
|
+
}
|
|
29
|
+
.input-block-level {
|
|
30
|
+
display: block;
|
|
31
|
+
width: 100%;
|
|
32
|
+
min-height: 30px;
|
|
33
|
+
-webkit-box-sizing: border-box;
|
|
34
|
+
-moz-box-sizing: border-box;
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
}
|
|
37
|
+
.alert {
|
|
38
|
+
padding: 8px 35px 8px 14px;
|
|
39
|
+
margin-bottom: 20px;
|
|
40
|
+
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
|
41
|
+
background-color: #fcf8e3;
|
|
42
|
+
border: 1px solid #fbeed5;
|
|
43
|
+
-webkit-border-radius: 4px;
|
|
44
|
+
-moz-border-radius: 4px;
|
|
45
|
+
border-radius: 4px;
|
|
46
|
+
color: #c09853;
|
|
47
|
+
}
|
|
48
|
+
.alert h4 {
|
|
49
|
+
margin: 0;
|
|
50
|
+
}
|
|
51
|
+
.alert .close {
|
|
52
|
+
position: relative;
|
|
53
|
+
top: -2px;
|
|
54
|
+
right: -21px;
|
|
55
|
+
line-height: 20px;
|
|
56
|
+
}
|
|
57
|
+
.alert-success {
|
|
58
|
+
background-color: #dff0d8;
|
|
59
|
+
border-color: #d6e9c6;
|
|
60
|
+
color: #468847;
|
|
61
|
+
}
|
|
62
|
+
.alert-danger,
|
|
63
|
+
.alert-error {
|
|
64
|
+
background-color: #f2dede;
|
|
65
|
+
border-color: #eed3d7;
|
|
66
|
+
color: #b94a48;
|
|
67
|
+
}
|
|
68
|
+
.alert-info {
|
|
69
|
+
background-color: #d9edf7;
|
|
70
|
+
border-color: #bce8f1;
|
|
71
|
+
color: #3a87ad;
|
|
72
|
+
}
|
|
73
|
+
.alert-block {
|
|
74
|
+
padding-top: 14px;
|
|
75
|
+
padding-bottom: 14px;
|
|
76
|
+
}
|
|
77
|
+
.alert-block > p,
|
|
78
|
+
.alert-block > ul {
|
|
79
|
+
margin-bottom: 0;
|
|
80
|
+
}
|
|
81
|
+
.alert-block p + p {
|
|
82
|
+
margin-top: 5px;
|
|
83
|
+
}
|
|
84
|
+
.close {
|
|
85
|
+
float: right;
|
|
86
|
+
font-size: 20px;
|
|
87
|
+
font-weight: bold;
|
|
88
|
+
line-height: 20px;
|
|
89
|
+
color: #000000;
|
|
90
|
+
text-shadow: 0 1px 0 #ffffff;
|
|
91
|
+
opacity: 0.2;
|
|
92
|
+
filter: alpha(opacity=20);
|
|
93
|
+
}
|
|
94
|
+
.close:hover {
|
|
95
|
+
color: #000000;
|
|
96
|
+
text-decoration: none;
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
opacity: 0.4;
|
|
99
|
+
filter: alpha(opacity=40);
|
|
100
|
+
}
|
|
101
|
+
button.close {
|
|
102
|
+
padding: 0;
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
background: transparent;
|
|
105
|
+
border: 0;
|
|
106
|
+
-webkit-appearance: none;
|
|
107
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Ecommerce
|
|
3
|
+
class ApplicationController < ::ActionController::Base
|
|
4
|
+
include ::Locomotive::Ecommerce::EcommerceHelper
|
|
5
|
+
|
|
6
|
+
before_filter :set_current_site
|
|
7
|
+
|
|
8
|
+
def authenticate_user!
|
|
9
|
+
if current_user(self) == nil
|
|
10
|
+
flash[:error] = "Authentication needed. Please log in to continue."
|
|
11
|
+
redirect_to cart_path
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def locomotive_user?
|
|
16
|
+
locomotive_account_signed_in?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def fetch_site
|
|
22
|
+
if Locomotive.config.multi_sites?
|
|
23
|
+
@current_site ||= ::Locomotive::Site.match_domain(request.host).first
|
|
24
|
+
else
|
|
25
|
+
@current_site ||= ::Locomotive::Site.first
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def set_current_site
|
|
30
|
+
Thread.current[:site] = fetch_site
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Ecommerce
|
|
3
|
+
class CartController < ::Locomotive::Ecommerce::ApplicationController
|
|
4
|
+
def update
|
|
5
|
+
@cart = Cart.find(params[:id])
|
|
6
|
+
@cart.update_from_params(params)
|
|
7
|
+
flash[:success] = 'Updated cart'
|
|
8
|
+
redirect_to cart_path
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Ecommerce
|
|
3
|
+
class OrderController < ::Locomotive::Ecommerce::ApplicationController
|
|
4
|
+
def create
|
|
5
|
+
@order = current_user_cart(self).add_product_by_sku(params[:item_sku])
|
|
6
|
+
flash[:success] = 'Added product to cart'
|
|
7
|
+
redirect_to cart_path
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def destroy
|
|
11
|
+
@order = current_user_cart(self).remove_product_by_sku(
|
|
12
|
+
params[:product_id])
|
|
13
|
+
flash[:success] = 'Removed product from cart'
|
|
14
|
+
redirect_to cart_path
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'stripe'
|
|
2
|
+
|
|
3
|
+
module Locomotive
|
|
4
|
+
module Ecommerce
|
|
5
|
+
class PurchaseController < ::Locomotive::Ecommerce::ApplicationController
|
|
6
|
+
before_filter :authenticate_user!, except: [:do_new_purchase]
|
|
7
|
+
|
|
8
|
+
def create
|
|
9
|
+
@purchase = current_user_cart(self).purchase
|
|
10
|
+
@purchase.shipping_info = params[:shipping_info]
|
|
11
|
+
@purchase.shipping_method = params[:shipping_method]
|
|
12
|
+
if @purchase.save
|
|
13
|
+
redirect_to confirm_order_path
|
|
14
|
+
return
|
|
15
|
+
elsif @purchase.errors.any?
|
|
16
|
+
flash_ar = []
|
|
17
|
+
@purchase.errors.full_messages.each { |msg| flash_ar << msg }
|
|
18
|
+
flash[:error] = flash_ar.join(', ')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
redirect_to checkout_path
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.complete(purchase_id, user, cart, stripeToken)
|
|
25
|
+
purchase = Purchase.where(_id: purchase_id).first
|
|
26
|
+
|
|
27
|
+
#Reset user cart
|
|
28
|
+
purchase.cart.user_id = nil
|
|
29
|
+
purchase.cart.save!
|
|
30
|
+
new_cart = Cart.create
|
|
31
|
+
new_cart.user_id = user.id
|
|
32
|
+
new_cart.save!
|
|
33
|
+
|
|
34
|
+
#complete purchase
|
|
35
|
+
purchase.stripe_token = stripeToken
|
|
36
|
+
purchase.complete
|
|
37
|
+
purchase.completed = true
|
|
38
|
+
purchase.user_id = user.id
|
|
39
|
+
purchase.save!
|
|
40
|
+
PurchaseMailer.purchase_confirmation(user, purchase).deliver
|
|
41
|
+
after_purchase_hook(purchase, user)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def self.after_purchase_hook(purchase, user)
|
|
47
|
+
|
|
48
|
+
site = Thread.current[:site]
|
|
49
|
+
cxt = site.plugin_object_for_id('ecommerce').js3_context
|
|
50
|
+
cxt['user'] = user
|
|
51
|
+
cxt['purchase'] = purchase
|
|
52
|
+
last = cxt.eval(Engine.config_or_default('after_purchase_hook'))
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'locomotive/ecommerce/plugin/inventory_interface'
|
|
2
|
+
|
|
3
|
+
module Locomotive
|
|
4
|
+
module Ecommerce
|
|
5
|
+
module EcommerceHelper
|
|
6
|
+
include ::Locomotive::Ecommerce::EcommerceCartHelper
|
|
7
|
+
include ::Locomotive::Ecommerce::EcommerceUrlHelper
|
|
8
|
+
include ::Locomotive::Ecommerce::InventoryInterface
|
|
9
|
+
|
|
10
|
+
# User
|
|
11
|
+
def current_user(controller)
|
|
12
|
+
if controller.session[:user_id]
|
|
13
|
+
site = Thread.current[:site]
|
|
14
|
+
user_from_plugin = site.plugin_object_for_id('identity_plugin').js3_context['identity_plugin_users']
|
|
15
|
+
@current_user ||= user_from_plugin.find(controller.session[:user_id])
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# View Helper
|
|
20
|
+
def as_currency(val)
|
|
21
|
+
"$#{'%.2f' % val}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Ecommerce
|
|
3
|
+
module EcommerceUrlHelper
|
|
4
|
+
##################
|
|
5
|
+
# configured urls
|
|
6
|
+
##################
|
|
7
|
+
def cart_path(*args)
|
|
8
|
+
Engine.config_or_default('cart_url')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def checkout_path
|
|
12
|
+
Engine.config_or_default('checkout_url')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def confirm_order_path
|
|
16
|
+
Engine.config_or_default('confirm_order_url')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def purchases_path
|
|
20
|
+
Engine.config_or_default('purchases_url')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def post_checkout_path
|
|
24
|
+
Engine.config_or_default('post_checkout_url')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
###########################
|
|
28
|
+
# non-configured urls
|
|
29
|
+
# (provided by the engine)
|
|
30
|
+
# #########################
|
|
31
|
+
def cart_update_path(stem, cart)
|
|
32
|
+
"#{stem}cart/#{cart.id}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def add_to_cart_path(stem, product)
|
|
36
|
+
"#{stem}add_to_cart/#{product.id}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def remove_from_cart_path(stem, product_id)
|
|
40
|
+
"#{stem}remove_from_cart/#{product_id}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def checkout_update_path(stem, purchase)
|
|
44
|
+
"#{stem}checkout/#{purchase.id}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def checkout_index_path(stem)
|
|
48
|
+
"#{stem}checkout"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Ecommerce
|
|
3
|
+
class PurchaseMailer < ActionMailer::Base
|
|
4
|
+
default from: Engine.config_or_default('contact')
|
|
5
|
+
|
|
6
|
+
# Subject can be set in your I18n file at config/locales/en.yml
|
|
7
|
+
# with the following lookup:
|
|
8
|
+
#
|
|
9
|
+
# en.purchase_mailer.purchase_confirmation.subject
|
|
10
|
+
#
|
|
11
|
+
def purchase_confirmation(user, purchase)
|
|
12
|
+
@purchase = purchase
|
|
13
|
+
@contact = Engine.config_or_default('contact')
|
|
14
|
+
@shop_name = Engine.config_or_default('shop_name')
|
|
15
|
+
mail to: user.email, subject: "Purchase Confirmation"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Ecommerce
|
|
3
|
+
class Cart
|
|
4
|
+
include ::Locomotive::Ecommerce::EcommerceHelper
|
|
5
|
+
include Mongoid::Document
|
|
6
|
+
field :user_id, :type => ::BSON::ObjectId
|
|
7
|
+
belongs_to :purchase, :class_name => "::Locomotive::Ecommerce::Purchase"
|
|
8
|
+
has_many :orders, :class_name => "::Locomotive::Ecommerce::Order"
|
|
9
|
+
|
|
10
|
+
def add_product_by_sku(sku)
|
|
11
|
+
already_existing_order = orders.where(:sku => sku)
|
|
12
|
+
if already_existing_order.count > 0
|
|
13
|
+
order = already_existing_order.first
|
|
14
|
+
order.quantity += 1
|
|
15
|
+
else
|
|
16
|
+
order = Order.new
|
|
17
|
+
order.sku = sku
|
|
18
|
+
order.cart = self
|
|
19
|
+
end
|
|
20
|
+
order.save!
|
|
21
|
+
return order
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def remove_product_by_sku(sku)
|
|
25
|
+
order = orders.where(:sku => sku)
|
|
26
|
+
order.destroy if order
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def purchase_total
|
|
30
|
+
total = 0
|
|
31
|
+
orders.each { |order| total += order.price }
|
|
32
|
+
return total
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def estimated_tax
|
|
36
|
+
purchase_total * ((Engine.config_or_default('estimated_tax_rate').to_f/100))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def subtotal_est_tax
|
|
40
|
+
purchase_total + estimated_tax + extras_total
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def add_extras_js
|
|
44
|
+
ext = Engine.config_or_default('edit_extra')
|
|
45
|
+
return nil unless ext
|
|
46
|
+
site = Thread.current[:site]
|
|
47
|
+
cxt = site.plugin_object_for_id('inventory').js3_context
|
|
48
|
+
cxt['purchase_total'] = purchase_total
|
|
49
|
+
cxt['orders'] = orders
|
|
50
|
+
js = cxt.eval(ext)
|
|
51
|
+
|
|
52
|
+
return js
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def extras_total
|
|
56
|
+
ext = 0
|
|
57
|
+
arr = add_extras_js
|
|
58
|
+
# Adding all the values returned by the javascript function
|
|
59
|
+
if arr
|
|
60
|
+
arr.values.each do |value|
|
|
61
|
+
ext += value
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
return ext
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def extras
|
|
69
|
+
orders ? arr = add_extras_js : ''
|
|
70
|
+
# Converting the prices to currency
|
|
71
|
+
extras_hash = Hash[arr.map {|k,v| [k,as_currency(v) ]}]
|
|
72
|
+
extras_hash.to_liquid
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def update_from_params(params)
|
|
76
|
+
to_update = Order.find(params[:order_ids])
|
|
77
|
+
count = 0
|
|
78
|
+
to_update.each do |order|
|
|
79
|
+
order.quantity = params[:quantity_ids][count] if order.cart_id == id
|
|
80
|
+
count += 1
|
|
81
|
+
order.save!
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# merge in contents of another cart
|
|
86
|
+
def merge(cart)
|
|
87
|
+
cart.orders.each do |order|
|
|
88
|
+
(1..order.quantity).each { |c| add_product_by_sku(order.sku) }
|
|
89
|
+
cart.remove_product_by_sku(order.sku)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def valid_stock?
|
|
94
|
+
orders.each { |order| return false if order.out_of_stock? }
|
|
95
|
+
return true
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def to_liquid
|
|
99
|
+
CartDrop.new(self)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# self methods
|
|
103
|
+
def self.for_user(id)
|
|
104
|
+
cart = where(:user_id => id).first || create(:user_id => id)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def self.find_or_create(id, session)
|
|
108
|
+
cart_id = session[:cart_id]
|
|
109
|
+
if cart_id != nil
|
|
110
|
+
cart = where(:_id => cart_id).first
|
|
111
|
+
|
|
112
|
+
if cart == nil
|
|
113
|
+
session[:cart_id] = nil
|
|
114
|
+
return find_or_create(id, session)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
return cart if id == nil
|
|
118
|
+
|
|
119
|
+
session[:cart_id] = nil
|
|
120
|
+
user_cart = Cart.for_user(id)
|
|
121
|
+
user_cart.merge(cart)
|
|
122
|
+
cart.destroy
|
|
123
|
+
return user_cart
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
return Cart.for_user(id) if id != nil
|
|
127
|
+
|
|
128
|
+
cart = create
|
|
129
|
+
cart.save!
|
|
130
|
+
session[:cart_id] = cart.id.to_s
|
|
131
|
+
return cart
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
class CartDrop < ::Liquid::Drop
|
|
136
|
+
def initialize(source)
|
|
137
|
+
@source = source
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def line_items
|
|
141
|
+
@source.orders
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def id
|
|
145
|
+
@source.id.to_s
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def extras
|
|
149
|
+
@source.extras
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
[:purchase_total, :estimated_tax, :subtotal_est_tax, :extras_total].each do |method|
|
|
153
|
+
define_method(method) {"%0.2f" % @source.send(method).round(2)}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
delegate :valid_stock?, to: :@source
|
|
157
|
+
|
|
158
|
+
protected
|
|
159
|
+
attr_accessor :source
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|