stall 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 +7 -0
- data/Rakefile +22 -0
- data/app/assets/javascripts/stall.coffee +12 -0
- data/app/assets/javascripts/stall/add-to-cart-form.coffee +22 -0
- data/app/assets/javascripts/stall/application.js +13 -0
- data/app/assets/javascripts/stall/carts.js +2 -0
- data/app/assets/stylesheets/stall/application.css +15 -0
- data/app/assets/stylesheets/stall/carts.css +4 -0
- data/app/controllers/stall/application_controller.rb +18 -0
- data/app/controllers/stall/carts_controller.rb +28 -0
- data/app/controllers/stall/checkout/steps_controller.rb +32 -0
- data/app/controllers/stall/checkouts_controller.rb +9 -0
- data/app/controllers/stall/line_items_controller.rb +26 -0
- data/app/helpers/stall/add_to_cart_helper.rb +10 -0
- data/app/helpers/stall/application_helper.rb +4 -0
- data/app/helpers/stall/cart_helper.rb +28 -0
- data/app/helpers/stall/checkout_helper.rb +7 -0
- data/app/models/stall.rb +5 -0
- data/app/models/stall/address.rb +5 -0
- data/app/models/stall/address_ownership.rb +15 -0
- data/app/models/stall/cart.rb +14 -0
- data/app/models/stall/customer.rb +5 -0
- data/app/models/stall/line_item.rb +45 -0
- data/app/models/stall/product_list.rb +56 -0
- data/app/models/stall/shipment.rb +15 -0
- data/app/models/stall/shipping_method.rb +5 -0
- data/app/services/stall/add_to_cart_service.rb +52 -0
- data/app/services/stall/base_service.rb +4 -0
- data/app/services/stall/shipping_fee_calculator_service.rb +21 -0
- data/app/views/checkout/steps/_informations.html.haml +41 -0
- data/app/views/checkout/steps/_payment_method.html.haml +1 -0
- data/app/views/checkout/steps/_shipping_method.html.haml +10 -0
- data/app/views/layouts/stall/application.html.erb +15 -0
- data/app/views/stall/carts/show.html.haml +23 -0
- data/app/views/stall/checkout/steps/show.html.haml +1 -0
- data/app/views/stall/line_items/_add_error.html.haml +17 -0
- data/app/views/stall/line_items/_added.html.haml +23 -0
- data/app/views/stall/line_items/_form.html.haml +12 -0
- data/config/locales/stall.en.yml +60 -0
- data/config/locales/stall.fr.yml +60 -0
- data/db/migrate/20151103154313_create_stall_line_items.rb +19 -0
- data/db/migrate/20160118121116_create_stall_product_lists.rb +15 -0
- data/db/migrate/20160118124016_create_customers.rb +11 -0
- data/db/migrate/20160122143746_create_stall_addresses.rb +17 -0
- data/db/migrate/20160122143748_create_stall_address_ownerships.rb +17 -0
- data/db/migrate/20160124014144_create_stall_shipping_methods.rb +10 -0
- data/db/migrate/20160124020313_create_stall_shipments.rb +17 -0
- data/lib/generators/stall/checkout/step/step_generator.rb +20 -0
- data/lib/generators/stall/checkout/step/templates/step.rb.erb +2 -0
- data/lib/generators/stall/checkout/wizard/templates/wizard.rb.erb +3 -0
- data/lib/generators/stall/checkout/wizard/wizard_generator.rb +20 -0
- data/lib/generators/stall/install/install_generator.rb +17 -0
- data/lib/generators/stall/install/templates/initializer.rb +47 -0
- data/lib/stall.rb +38 -0
- data/lib/stall/addressable.rb +44 -0
- data/lib/stall/checkout.rb +15 -0
- data/lib/stall/checkout/informations_checkout_step.rb +41 -0
- data/lib/stall/checkout/payment_checkout_step.rb +6 -0
- data/lib/stall/checkout/payment_method_checkout_step.rb +6 -0
- data/lib/stall/checkout/shipping_method_checkout_step.rb +20 -0
- data/lib/stall/checkout/step.rb +47 -0
- data/lib/stall/checkout/wizard.rb +85 -0
- data/lib/stall/config.rb +27 -0
- data/lib/stall/engine.rb +30 -0
- data/lib/stall/rails/currency_helper.rb +26 -0
- data/lib/stall/rails/routing_mapper.rb +13 -0
- data/lib/stall/routes.rb +27 -0
- data/lib/stall/sellable.rb +8 -0
- data/lib/stall/sellable/mixin.rb +17 -0
- data/lib/stall/sellable/model.rb +46 -0
- data/lib/stall/shipping.rb +16 -0
- data/lib/stall/shipping/calculator.rb +28 -0
- data/lib/stall/shipping/config.rb +17 -0
- data/lib/stall/shipping/country_weight_table_calculator.rb +69 -0
- data/lib/stall/shipping/free_shipping_calculator.rb +23 -0
- data/lib/stall/utils.rb +22 -0
- data/lib/stall/utils/config_dsl.rb +19 -0
- data/lib/stall/version.rb +3 -0
- data/lib/tasks/stall_tasks.rake +4 -0
- metadata +362 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9caa6a04c98d0ebd94ac128e27de7e7ea7797a55
|
4
|
+
data.tar.gz: 024289eded82201eaf2a5c0dcc3c1cde5f071011
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 76d45836e3b79f5ab7bde8809ff4ed35779d1bfb5dc28aeb99985b8b6b31ad9f1f3aa82b0870b32a770c293322ede4ccdad39fcfaa24d1ebce474f9cc541b9b1
|
7
|
+
data.tar.gz: ea9f53aca3a34913111afc301c48616912fc9ff39831fe2679fa0dcfb77123316236fbafdc87a980cd9d4adb5a5164680ff8c8f3e94314f78f9d80fcb4f1e9c0
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 vala
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
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 = 'Stall'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
load 'rails/tasks/statistics.rake'
|
20
|
+
|
21
|
+
Bundler::GemHelper.install_tasks
|
22
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Stall.AddToCartForm extends Vertebra.View
|
2
|
+
@create = ($el) ->
|
3
|
+
return if $el.data('stall.add-to-cart-form')
|
4
|
+
instance = new Stall.AddToCartForm(el: $el)
|
5
|
+
$el.data('stall.add-to-cart-form', instance)
|
6
|
+
|
7
|
+
events:
|
8
|
+
'ajax:success': 'onSuccess'
|
9
|
+
|
10
|
+
onSuccess: (e, resp) ->
|
11
|
+
@$modal = $(resp).appendTo('body').modal()
|
12
|
+
@updateTotalQuantityCounter()
|
13
|
+
|
14
|
+
updateTotalQuantityCounter: ->
|
15
|
+
quantity = @$modal.data('cart-total-quantity')
|
16
|
+
|
17
|
+
if ($counter = $('[data-cart-quantity-counter]')).length
|
18
|
+
$counter.text(quantity)
|
19
|
+
|
20
|
+
Stall.onDomReady ->
|
21
|
+
$('body').on 'ajax:beforeSend', '[data-add-to-cart-form]', (e) ->
|
22
|
+
Stall.AddToCartForm.create($(e.currentTarget))
|
@@ -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 any plugin's vendor/assets/javascripts directory 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/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
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 any plugin's vendor/assets/stylesheets directory 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 bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Stall
|
2
|
+
class ApplicationController < Stall.config.application_controller_ancestor.constantize
|
3
|
+
layout :_set_stall_layout
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
# Enforce app-level layout by defaulting to the "application" layout if
|
8
|
+
# no parent layout was set before, thus avoiding the "stall/application"
|
9
|
+
# layout to be rendered
|
10
|
+
def _set_stall_layout
|
11
|
+
parent_controller = self.class.ancestors.find do |ancestor|
|
12
|
+
!ancestor.name.match(/^Stall::/)
|
13
|
+
end
|
14
|
+
|
15
|
+
parent_controller._layout ||= 'application'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Stall
|
2
|
+
class CartsController < ApplicationController
|
3
|
+
before_action :load_cart
|
4
|
+
|
5
|
+
def show
|
6
|
+
end
|
7
|
+
|
8
|
+
def update
|
9
|
+
if @cart.update_attributes(cart_params)
|
10
|
+
flash[:success] = t('stall.carts.flashes.update.success')
|
11
|
+
redirect_to cart_path(@cart)
|
12
|
+
else
|
13
|
+
flash[:error] = t('stall.carts.flashes.update.error')
|
14
|
+
render 'show'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def load_cart
|
21
|
+
@cart = Cart.find_by_token(params[:id])
|
22
|
+
end
|
23
|
+
|
24
|
+
def cart_params
|
25
|
+
params.require(:cart).permit(line_items_attributes: [:id, :quantity])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Stall
|
2
|
+
module Checkout
|
3
|
+
class StepsController < Stall::ApplicationController
|
4
|
+
include Stall::CheckoutHelper
|
5
|
+
|
6
|
+
before_action :load_step
|
7
|
+
|
8
|
+
def show
|
9
|
+
@step.prepare
|
10
|
+
end
|
11
|
+
|
12
|
+
def update
|
13
|
+
if @step.process
|
14
|
+
@wizard.validate_current_step!
|
15
|
+
redirect_to step_path(@cart)
|
16
|
+
else
|
17
|
+
@step.prepare
|
18
|
+
flash[:error] = t("stall.checkout.#{ @wizard.current_step_name }.error")
|
19
|
+
render 'show'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def load_step
|
26
|
+
@cart = Stall::Cart.find_by_token(params[:cart_id])
|
27
|
+
@wizard = @cart.wizard.new(@cart)
|
28
|
+
@step = @wizard.current_step.new(@cart, params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Stall
|
2
|
+
class LineItemsController < ApplicationController
|
3
|
+
def create
|
4
|
+
service = Stall::AddToCartService.new(cart, line_item_params)
|
5
|
+
|
6
|
+
if service.call
|
7
|
+
@quantity = params[:line_item][:quantity].to_i
|
8
|
+
@line_item = service.line_item
|
9
|
+
render partial: 'added'
|
10
|
+
else
|
11
|
+
@line_item = service.line_item
|
12
|
+
render partial: 'add_error'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def line_item_params
|
19
|
+
params.require(:line_item).permit(:sellable_type, :sellable_id, :quantity)
|
20
|
+
end
|
21
|
+
|
22
|
+
def cart
|
23
|
+
@cart ||= Stall::Cart.find_by_token(params[:cart_id])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Stall
|
2
|
+
module CartHelper
|
3
|
+
def current_cart
|
4
|
+
RequestStore.store[cart_store_key_for(:default)] ||= load_current_cart
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def load_current_cart
|
10
|
+
if (cart_token = session[cart_store_key_for(:default)])
|
11
|
+
if (cart = Stall::Cart.find_by_token(cart_token))
|
12
|
+
return cart
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# If no token was stored or the token does not exist anymore, create a
|
17
|
+
# new cart and store the new token
|
18
|
+
#
|
19
|
+
Stall::Cart.create!.tap do |cart|
|
20
|
+
session[cart_store_key_for(:default)] = cart.token
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def cart_store_key_for(identifier)
|
25
|
+
['stall', 'cart', identifier.to_s].join('.')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/app/models/stall.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Stall
|
2
|
+
class AddressOwnership < ActiveRecord::Base
|
3
|
+
belongs_to :address
|
4
|
+
accepts_nested_attributes_for :address
|
5
|
+
|
6
|
+
belongs_to :addressable, polymorphic: true
|
7
|
+
|
8
|
+
def type_name
|
9
|
+
types = []
|
10
|
+
types << :billing if billing
|
11
|
+
types << :shipping if shipping
|
12
|
+
types.join('-')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Stall
|
2
|
+
class Cart < Stall::ProductList
|
3
|
+
include Stall::Addressable
|
4
|
+
|
5
|
+
has_one :shipment, dependent: :destroy, inverse_of: :cart
|
6
|
+
accepts_nested_attributes_for :shipment
|
7
|
+
|
8
|
+
def total_weight
|
9
|
+
line_items.reduce(0) do |total, line_item|
|
10
|
+
total + (line_item.weight || Stall.config.default_product_weight)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Stall
|
2
|
+
class LineItem < ActiveRecord::Base
|
3
|
+
store_accessor :data, :weight
|
4
|
+
|
5
|
+
monetize :unit_eot_price_cents, :unit_price_cents,
|
6
|
+
:eot_price_cents, :price_cents,
|
7
|
+
with_model_currency: :currency, allow_nil: true
|
8
|
+
|
9
|
+
belongs_to :sellable, polymorphic: true
|
10
|
+
belongs_to :product_list
|
11
|
+
|
12
|
+
validates :name, :unit_price, :unit_eot_price, :vat_rate, :price, :quantity,
|
13
|
+
:eot_price, :sellable, presence: true
|
14
|
+
|
15
|
+
validates :unit_price, :unit_eot_price, :vat_rate, :price, :eot_price,
|
16
|
+
numericality: true
|
17
|
+
|
18
|
+
validates :quantity, numericality: { greater_than: 0 }
|
19
|
+
|
20
|
+
validate :stock_availability
|
21
|
+
|
22
|
+
before_validation :refresh_total_prices
|
23
|
+
|
24
|
+
def like?(other)
|
25
|
+
[:sellable_id, :sellable_type].all? do |property|
|
26
|
+
public_send(property) == other.public_send(property)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def currency
|
31
|
+
product_list.try(:currency) || Money.default_currency
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# TODO : Stocks availibility handling
|
37
|
+
def stock_availability
|
38
|
+
end
|
39
|
+
|
40
|
+
def refresh_total_prices
|
41
|
+
self.eot_price = unit_eot_price * quantity if unit_eot_price && quantity
|
42
|
+
self.price = unit_price * quantity if unit_price && quantity
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Stall
|
2
|
+
class ProductList < ActiveRecord::Base
|
3
|
+
has_secure_token
|
4
|
+
|
5
|
+
has_many :line_items, dependent: :destroy
|
6
|
+
accepts_nested_attributes_for :line_items
|
7
|
+
|
8
|
+
belongs_to :customer, class_name: 'Stall::Customer'
|
9
|
+
accepts_nested_attributes_for :customer
|
10
|
+
|
11
|
+
validates :type, presence: true
|
12
|
+
|
13
|
+
after_initialize :ensure_currency
|
14
|
+
after_initialize :ensure_state
|
15
|
+
|
16
|
+
def state
|
17
|
+
read_attribute(:state).try(:to_sym)
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset_state!
|
21
|
+
update_column(:state, wizard.steps.first)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_param
|
25
|
+
token
|
26
|
+
end
|
27
|
+
|
28
|
+
def total_quantity
|
29
|
+
line_items.map(&:quantity).sum
|
30
|
+
end
|
31
|
+
|
32
|
+
def wizard
|
33
|
+
@wizard ||= begin
|
34
|
+
wizard_name = Stall.config.default_checkout_wizard
|
35
|
+
|
36
|
+
if (wizard = Stall::Utils.try_load_constant(wizard_name))
|
37
|
+
wizard
|
38
|
+
else
|
39
|
+
raise Stall::Checkout::WizardNotFoundError.new,
|
40
|
+
"The checkout wizard #{ wizard_name } was not found. You must generate it " +
|
41
|
+
"with `rails g stall:wizard #{ wizard_name.underscore.gsub('_checkout_wizard', '') }`"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def ensure_currency
|
49
|
+
self.currency ||= Money.default_currency
|
50
|
+
end
|
51
|
+
|
52
|
+
def ensure_state
|
53
|
+
self.state ||= wizard.steps.first
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|