stall 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/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
@@ -0,0 +1,15 @@
|
|
1
|
+
module Stall
|
2
|
+
class Shipment < ActiveRecord::Base
|
3
|
+
belongs_to :cart
|
4
|
+
belongs_to :shipping_method
|
5
|
+
|
6
|
+
monetize :eot_price_cents, :price_cents,
|
7
|
+
with_model_currency: :currency, allow_nil: true
|
8
|
+
|
9
|
+
validates :cart, presence: true
|
10
|
+
|
11
|
+
def currency
|
12
|
+
cart.try(:currency) || Money.default_currency
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Stall
|
2
|
+
class AddToCartService < Stall::BaseService
|
3
|
+
attr_reader :cart, :params
|
4
|
+
|
5
|
+
def initialize(cart, params)
|
6
|
+
@cart = cart
|
7
|
+
@params = params
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
return false unless line_item.valid?
|
12
|
+
|
13
|
+
unless assemble_identical_line_items
|
14
|
+
cart.line_items << line_item
|
15
|
+
end
|
16
|
+
|
17
|
+
cart.save
|
18
|
+
end
|
19
|
+
|
20
|
+
def line_item
|
21
|
+
@line_item ||= sellable.to_line_item.tap do |line_item|
|
22
|
+
line_item.quantity = params[:quantity]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def assemble_identical_line_items
|
29
|
+
# Find an existing line item which is like our new one
|
30
|
+
existing_line_item = cart.line_items.find do |li|
|
31
|
+
line_item.like?(li)
|
32
|
+
end
|
33
|
+
|
34
|
+
# If we found one, we assemble both line items into the old one, to avoid
|
35
|
+
# duplicating the same sellables in the product list
|
36
|
+
if existing_line_item
|
37
|
+
existing_line_item.quantity += line_item.quantity
|
38
|
+
@line_item = existing_line_item
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def sellable
|
45
|
+
@sellable ||= sellable_class.find(params[:sellable_id])
|
46
|
+
end
|
47
|
+
|
48
|
+
def sellable_class
|
49
|
+
@sellable_class ||= params[:sellable_type].camelize.constantize
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Stall
|
2
|
+
class ShippingFeeCalculatorService < Stall::BaseService
|
3
|
+
attr_reader :cart
|
4
|
+
|
5
|
+
def initialize(cart)
|
6
|
+
@cart = cart
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
return unless cart.shipment && cart.shipment.shipping_method
|
11
|
+
|
12
|
+
calculator_identifier = cart.shipment.shipping_method.identifier
|
13
|
+
calculator_class = Stall::Shipping.calculators[calculator_identifier]
|
14
|
+
|
15
|
+
if calculator_class
|
16
|
+
calculator = calculator_class.new(cart, cart.shipment.shipping_method)
|
17
|
+
cart.shipment.update_attributes(price: calculator.price)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
%h1= t('stall.checkout.informations.title')
|
2
|
+
|
3
|
+
= simple_form_for cart, as: :cart, url: step_path(cart) do |form|
|
4
|
+
= form.fields_for :customer do |customer_form|
|
5
|
+
= customer_form.input :email
|
6
|
+
|
7
|
+
= form.fields_for :address_ownerships, [cart.address_ownership_for(:shipping)] do |ownership_form|
|
8
|
+
%fieldset{ data: { :'address-form' => :shipping } }
|
9
|
+
= ownership_form.hidden_field :shipping
|
10
|
+
|
11
|
+
= ownership_form.fields_for :address do |address_form|
|
12
|
+
= address_form.input :civility
|
13
|
+
= address_form.input :first_name
|
14
|
+
= address_form.input :last_name
|
15
|
+
= address_form.input :address
|
16
|
+
= address_form.input :address_details
|
17
|
+
= address_form.input :zip
|
18
|
+
= address_form.input :city
|
19
|
+
= address_form.input :country
|
20
|
+
= address_form.input :phone
|
21
|
+
|
22
|
+
= form.input :use_another_address_for_billing do
|
23
|
+
= check_box_tag :use_another_address_for_billing, '1', params[:use_another_address_for_billing]
|
24
|
+
|
25
|
+
= form.fields_for :address_ownerships, [cart.address_ownership_for(:billing)] do |ownership_form|
|
26
|
+
%fieldset{ data: { :'address-form' => :billing } }
|
27
|
+
= ownership_form.hidden_field :billing
|
28
|
+
|
29
|
+
= ownership_form.fields_for :address do |address_form|
|
30
|
+
= address_form.input :civility
|
31
|
+
= address_form.input :first_name
|
32
|
+
= address_form.input :last_name
|
33
|
+
= address_form.input :address
|
34
|
+
= address_form.input :address_details
|
35
|
+
= address_form.input :zip
|
36
|
+
= address_form.input :city
|
37
|
+
= address_form.input :country
|
38
|
+
= address_form.input :phone
|
39
|
+
|
40
|
+
%button.btn.btn-primary{ type: 'submit' }
|
41
|
+
= t('stall.checkout.informations.validate')
|
@@ -0,0 +1 @@
|
|
1
|
+
%h1= t('stall.checkout.payment_method.title')
|
@@ -0,0 +1,10 @@
|
|
1
|
+
%h1= t('stall.checkout.shipping_method.title')
|
2
|
+
|
3
|
+
= simple_form_for cart, as: :cart, url: step_path(cart) do |form|
|
4
|
+
= form.fields_for :shipment do |shipment_fields|
|
5
|
+
= shipment_fields.association :shipping_method, as: :radio_buttons
|
6
|
+
|
7
|
+
.form-actions
|
8
|
+
%button.btn.btn-primary{ type: 'submit' }
|
9
|
+
= t('stall.checkout.shipping_method.validate')
|
10
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Stall</title>
|
5
|
+
<meta charset="utf-8">
|
6
|
+
<%= stylesheet_link_tag "stall/application", media: "all" %>
|
7
|
+
<%= javascript_include_tag "stall/application" %>
|
8
|
+
<%= csrf_meta_tags %>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
|
12
|
+
<%= yield %>
|
13
|
+
|
14
|
+
</body>
|
15
|
+
</html>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
.cart-recap{ data: { :'cart-recap' => true } }
|
2
|
+
= simple_form_for @cart, url: cart_path(@cart), as: :cart do |form|
|
3
|
+
%table.table.table-striped
|
4
|
+
%thead
|
5
|
+
%tr
|
6
|
+
%th= Stall::LineItem.human_attribute_name(:name)
|
7
|
+
%th= Stall::LineItem.human_attribute_name(:unit_price)
|
8
|
+
%th= Stall::LineItem.human_attribute_name(:quantity)
|
9
|
+
%th= Stall::LineItem.human_attribute_name(:price)
|
10
|
+
%tbody
|
11
|
+
= form.fields_for :line_items do |line_item_fields|
|
12
|
+
%tr{ data: { :'line-item-id' => line_item_fields.object.id } }
|
13
|
+
%td= line_item_fields.object.name
|
14
|
+
%td= line_item_fields.object.unit_price
|
15
|
+
%td= line_item_fields.input_field :quantity, class: 'form-control', data: { :'quantity-field' => true }
|
16
|
+
%td= line_item_fields.object.price
|
17
|
+
|
18
|
+
.form-actions
|
19
|
+
%button.btn.btn-default{ type: 'submit' }
|
20
|
+
= t('stall.carts.recap.update')
|
21
|
+
|
22
|
+
= link_to checkout_path(@cart), class: 'btn btn-primary' do
|
23
|
+
= t('stall.carts.recap.validate')
|
@@ -0,0 +1 @@
|
|
1
|
+
= render partial: "checkout/steps/#{ @wizard.current_step_name }", locals: { wizard: @wizard, cart: @cart }
|
@@ -0,0 +1,17 @@
|
|
1
|
+
.modal.danger.fade{ role: 'dialog', tabindex: '-1', data: { :'line-item' => @line_item.as_json(only: [:name, :quantity, :eot_price, :price, :vat_rate]) } }
|
2
|
+
.modal-dialog
|
3
|
+
.modal-content
|
4
|
+
.modal-header
|
5
|
+
%button.close{ type: 'button', :'aria-label' => 'Close', data: { dismiss: 'modal' } }
|
6
|
+
%span{ :'aria-hidden' => 'true' }
|
7
|
+
×
|
8
|
+
%h4.modal-title
|
9
|
+
= t('stall.line_items.add_error.title')
|
10
|
+
|
11
|
+
.modal-body
|
12
|
+
%p.text-danger
|
13
|
+
= t('stall.line_items.add_error.description')
|
14
|
+
|
15
|
+
.modal-footer
|
16
|
+
%button.btn.btn-default{ type: 'button', data: { dismiss: 'modal' } }
|
17
|
+
= t('stall.shared.close')
|
@@ -0,0 +1,23 @@
|
|
1
|
+
.modal.fade{ role: 'dialog', tabindex: '-1', data: { :'cart' => { :'total-quantity' => current_cart.total_quantity } } }
|
2
|
+
.modal-dialog
|
3
|
+
.modal-content
|
4
|
+
.modal-header
|
5
|
+
%button.close{ type: 'button', :'aria-label' => 'Close', data: { dismiss: 'modal' } }
|
6
|
+
%span{ :'aria-hidden' => 'true' }
|
7
|
+
×
|
8
|
+
%h4.modal-title
|
9
|
+
= t('stall.line_items.added.title')
|
10
|
+
|
11
|
+
.modal-body
|
12
|
+
= @line_item.name
|
13
|
+
x
|
14
|
+
= @quantity
|
15
|
+
\=
|
16
|
+
= number_to_currency(@line_item.unit_price * @quantity)
|
17
|
+
|
18
|
+
.modal-footer
|
19
|
+
%button.btn.btn-default{ type: 'button', data: { dismiss: 'modal' } }
|
20
|
+
= t('stall.line_items.added.continue_shopping')
|
21
|
+
|
22
|
+
= link_to cart_path(@cart), class: 'btn btn-primary' do
|
23
|
+
= t('stall.line_items.added.view_cart')
|
@@ -0,0 +1,12 @@
|
|
1
|
+
= simple_form_for line_item, as: :line_item, url: cart_line_items_path(cart), remote: true, data: { :'add-to-cart-form' => true } do |form|
|
2
|
+
= form.hidden_field :sellable_type
|
3
|
+
= form.hidden_field :sellable_id
|
4
|
+
|
5
|
+
.input-group
|
6
|
+
= form.input_field :quantity, spinner: false, value: 1, class: 'form-group'
|
7
|
+
|
8
|
+
%span.input-group-btn
|
9
|
+
%button.btn.btn-primary{ type: 'submit', style: 'font-size: 12px' }
|
10
|
+
%i.fa.fa-shopping-cart
|
11
|
+
= t('stall.line_items.form.add_to_cart')
|
12
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
en:
|
2
|
+
stall:
|
3
|
+
shared:
|
4
|
+
close: "Close"
|
5
|
+
|
6
|
+
carts:
|
7
|
+
flashes:
|
8
|
+
update:
|
9
|
+
success: "Your cart has successfully been updated"
|
10
|
+
error: "Your cart could not be updated, please check that all the fields are correctly filled"
|
11
|
+
recap:
|
12
|
+
update: "Update the cart"
|
13
|
+
validate: "Proceed to order"
|
14
|
+
|
15
|
+
line_items:
|
16
|
+
form:
|
17
|
+
add_to_cart: "Add to cart"
|
18
|
+
added:
|
19
|
+
title: "Votre produit a bien été ajouté à votre panier"
|
20
|
+
continue_shopping: "Continuer mes achats"
|
21
|
+
view_cart: "Voir mon panier"
|
22
|
+
add_error:
|
23
|
+
title: "Votre produit n'a pu être ajouté"
|
24
|
+
description: "Merci de vérifier que vous avez bien rempli la quantité avant d'ajouter le produit au panier."
|
25
|
+
|
26
|
+
checkout:
|
27
|
+
informations:
|
28
|
+
title: "Vos informations"
|
29
|
+
validate: "Valider mes adresses"
|
30
|
+
error: "Your informations could not be saved, please check that all the fields are correctly filled"
|
31
|
+
shipping_method:
|
32
|
+
title: "Mode de livraison"
|
33
|
+
validate: "Valider mon mode de livraison"
|
34
|
+
payment_method:
|
35
|
+
title: "Mode de paiement"
|
36
|
+
validate: "Valider mon mode de paiement"
|
37
|
+
payment:
|
38
|
+
title: "Paiement"
|
39
|
+
|
40
|
+
activerecord:
|
41
|
+
attributes:
|
42
|
+
stall/customer:
|
43
|
+
email: "E-mail address"
|
44
|
+
stall/line_item:
|
45
|
+
name: "Produit"
|
46
|
+
unit_price: "Prix unitaire"
|
47
|
+
unit_eot_price: "Prix unitaire HT"
|
48
|
+
price: "Prix"
|
49
|
+
eot_price: "Prix HT"
|
50
|
+
quantity: "Quantité"
|
51
|
+
stall/address:
|
52
|
+
civility: "Civility"
|
53
|
+
first_name: "First name"
|
54
|
+
last_name: "Last name"
|
55
|
+
address: "Adress"
|
56
|
+
address_details: "Address 2"
|
57
|
+
zip: "Zip code"
|
58
|
+
city: "City"
|
59
|
+
country: "Country"
|
60
|
+
phone: "Phone"
|
@@ -0,0 +1,60 @@
|
|
1
|
+
fr:
|
2
|
+
stall:
|
3
|
+
shared:
|
4
|
+
close: "Fermer"
|
5
|
+
|
6
|
+
carts:
|
7
|
+
flashes:
|
8
|
+
update:
|
9
|
+
success: "Votre panier a bien été mis à jour."
|
10
|
+
error: "Votre panier n'a pu être mis à jour, merci de vérifier les champs."
|
11
|
+
recap:
|
12
|
+
update: "Mettre à jour le panier"
|
13
|
+
validate: "Passer la commande"
|
14
|
+
|
15
|
+
line_items:
|
16
|
+
form:
|
17
|
+
add_to_cart: "Ajouter au panier"
|
18
|
+
added:
|
19
|
+
title: "Votre produit a bien été ajouté à votre panier"
|
20
|
+
continue_shopping: "Continuer mes achats"
|
21
|
+
view_cart: "Voir mon panier"
|
22
|
+
add_error:
|
23
|
+
title: "Votre produit n'a pu être ajouté"
|
24
|
+
description: "Merci de vérifier que vous avez bien rempli la quantité avant d'ajouter le produit au panier."
|
25
|
+
|
26
|
+
checkout:
|
27
|
+
informations:
|
28
|
+
title: "Vos informations"
|
29
|
+
validate: "Valider mes adresses"
|
30
|
+
error: "Vos informations n'ont pu être enregistrées, merci de vérifier les champs"
|
31
|
+
shipping_method:
|
32
|
+
title: "Mode de livraison"
|
33
|
+
validate: "Valider mon mode de livraison"
|
34
|
+
payment_method:
|
35
|
+
title: "Mode de paiement"
|
36
|
+
validate: "Valider mon mode de paiement"
|
37
|
+
payment:
|
38
|
+
title: "Paiement"
|
39
|
+
|
40
|
+
activerecord:
|
41
|
+
attributes:
|
42
|
+
stall/line_item:
|
43
|
+
name: "Produit"
|
44
|
+
unit_price: "Prix unitaire"
|
45
|
+
unit_eot_price: "Prix unitaire HT"
|
46
|
+
price: "Prix"
|
47
|
+
eot_price: "Prix HT"
|
48
|
+
quantity: "Quantité"
|
49
|
+
stall/customer:
|
50
|
+
email: "Adresse e-mail"
|
51
|
+
stall/address:
|
52
|
+
civility: "Civilité"
|
53
|
+
first_name: "Prénom"
|
54
|
+
last_name: "Nom"
|
55
|
+
address: "Adresse"
|
56
|
+
address_details: "Complément d'adresse"
|
57
|
+
zip: "Code postal"
|
58
|
+
city: "Ville"
|
59
|
+
country: "Pays"
|
60
|
+
phone: "Téléphone"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateStallLineItems < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :stall_line_items do |t|
|
4
|
+
t.references :sellable, polymorphic: true, index: true
|
5
|
+
t.references :product_list, index: true
|
6
|
+
|
7
|
+
t.string :name, null: false
|
8
|
+
t.integer :quantity, null: false
|
9
|
+
t.monetize :unit_eot_price, null: false, currency: { present: false }
|
10
|
+
t.monetize :unit_price, null: false, currency: { present: false }
|
11
|
+
t.monetize :eot_price, null: false, currency: { present: false }
|
12
|
+
t.monetize :price, null: false, currency: { present: false }
|
13
|
+
t.decimal :vat_rate, null: false
|
14
|
+
t.json :data
|
15
|
+
|
16
|
+
t.timestamps null: false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateStallProductLists < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :stall_product_lists do |t|
|
4
|
+
t.string :state, null: false
|
5
|
+
t.string :type, null: false
|
6
|
+
t.string :currency, null: false
|
7
|
+
t.references :customer, index: true
|
8
|
+
t.string :token, null: false
|
9
|
+
|
10
|
+
t.timestamps null: false
|
11
|
+
end
|
12
|
+
|
13
|
+
add_foreign_key :stall_line_items, :stall_product_lists, column: :product_list_id
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateStallAddresses < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :stall_addresses do |t|
|
4
|
+
t.integer :civility
|
5
|
+
t.string :first_name
|
6
|
+
t.string :last_name
|
7
|
+
t.text :address
|
8
|
+
t.text :address_details
|
9
|
+
t.string :zip
|
10
|
+
t.string :city
|
11
|
+
t.string :country
|
12
|
+
t.string :phone
|
13
|
+
|
14
|
+
t.timestamps null: false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|