stall 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +41 -3
- data/app/assets/javascripts/stall/add-to-cart-form.coffee +55 -3
- data/app/assets/javascripts/stall/addresses-fields.coffee +17 -0
- data/app/assets/javascripts/stall/remote-sign-in-form.coffee +55 -0
- data/app/assets/javascripts/stall.coffee +3 -1
- data/app/controllers/stall/application_controller.rb +1 -1
- data/app/controllers/stall/carts_controller.rb +1 -3
- data/app/controllers/stall/checkout/steps_controller.rb +18 -1
- data/app/helpers/stall/checkout_helper.rb +7 -8
- data/app/helpers/stall/customers_helper.rb +25 -0
- data/app/models/stall/models/cart.rb +2 -0
- data/app/models/stall/models/customer.rb +20 -0
- data/app/models/stall/models/product_list.rb +13 -0
- data/app/views/checkout/steps/_informations.html.haml +24 -35
- data/app/views/checkout/steps/_payment.html.haml +1 -45
- data/app/views/checkout/steps/_shipping_method.html.haml +0 -1
- data/app/views/stall/addresses/_fields.html.haml +16 -0
- data/app/views/stall/addresses/_nested_fields.html.haml +14 -0
- data/app/views/stall/carts/_cart.html.haml +45 -0
- data/app/views/stall/carts/show.html.haml +2 -2
- data/app/views/stall/customers/_fields.html.haml +32 -0
- data/app/views/stall/customers/_sign_in.html.haml +20 -0
- data/app/views/stall/line_items/_form.html.haml +2 -2
- data/app/views/stall/payments/_fields.html.haml +2 -0
- data/app/views/stall/shared/mailers/_cart.html.haml +31 -29
- data/app/views/stall/shipments/_fields.html.haml +2 -0
- data/config/locales/stall.fr.yml +35 -8
- data/lib/generators/stall/checkout/step/templates/step.rb.erb +9 -0
- data/lib/generators/stall/checkout/wizard/templates/wizard.rb.erb +1 -1
- data/lib/generators/stall/install/install_generator.rb +6 -0
- data/lib/generators/stall/install/templates/initializer.rb +10 -1
- data/lib/generators/stall/view/view_generator.rb +38 -0
- data/lib/stall/addressable.rb +2 -0
- data/lib/stall/addresses/copier_base.rb +24 -0
- data/lib/stall/addresses/copy_source_to_target.rb +45 -0
- data/lib/stall/addresses/prefill_target_from_source.rb +29 -0
- data/lib/stall/addresses.rb +9 -0
- data/lib/stall/cart_helper.rb +14 -1
- data/lib/stall/checkout/informations_checkout_step.rb +133 -14
- data/lib/stall/checkout/payment_checkout_step.rb +21 -1
- data/lib/stall/checkout/step.rb +22 -15
- data/lib/stall/checkout/step_form.rb +22 -8
- data/lib/stall/config.rb +17 -3
- data/lib/stall/engine.rb +6 -0
- data/lib/stall/payable.rb +9 -0
- data/lib/stall/payments/gateway.rb +16 -6
- data/lib/stall/payments/urls_config.rb +2 -2
- data/lib/stall/rails/routing_mapper.rb +4 -6
- data/lib/stall/routes.rb +4 -1
- data/lib/stall/sellable.rb +13 -3
- data/lib/stall/version.rb +1 -1
- data/lib/stall.rb +1 -0
- metadata +25 -6
- data/lib/stall/checkout/payment_method_checkout_step.rb +0 -9
- data/lib/stall/checkout/shipping_method_checkout_step.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 282c715e0c37f5b8581b410bcc70a30777973284
|
4
|
+
data.tar.gz: cc5283aa96ce4dd6cbb89678fc1ef73a812be26d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 256db2643c7db87598b7c55f6101b4c88b180ce7b27b17cb6d2596230cd36077b48265ef0b07e7b1c7cd8a5c45ce0addc58b5198fce5768075086c20bd02c6d7
|
7
|
+
data.tar.gz: 1cb10a57017e254f1fcabdf2e57e613fda6b1aee3f0392a74e0d7d7932eb4062554eae49e2100dc29f68940365da710b82192d1da67fe7322519cbd2af1e61f1
|
data/README.md
CHANGED
@@ -38,7 +38,16 @@ This will generate :
|
|
38
38
|
|
39
39
|
## Usage
|
40
40
|
|
41
|
-
|
41
|
+
In the following sections, you'll find the following informations :
|
42
|
+
|
43
|
+
1. [Making a model sellable](#1--Making-a-model-sellable)
|
44
|
+
2. [Configuring shop defaults](#2--Configuring-shop-defaults)
|
45
|
+
3. [Configuring the checkout flow](#3--Configuring-the-checkout-flow)
|
46
|
+
4. [Customizing views](#4--Customizing-views)
|
47
|
+
5. [Cleaning up aborted carts](#5--Cleaning-up-aborted-carts)
|
48
|
+
|
49
|
+
|
50
|
+
### 1. Making a model sellable
|
42
51
|
|
43
52
|
Stall allows you to make any model sellable by including the `Stall::Sellable`
|
44
53
|
mixin into your model :
|
@@ -58,7 +67,21 @@ You can now add the "Add to cart" button to your templates :
|
|
58
67
|
For more informations see the Wiki page :
|
59
68
|
[Allowing customers to add products to cart](https://github.com/rails-stall/stall/wiki/Allowing-customers-to-add-products-to-cart)
|
60
69
|
|
61
|
-
### Configuring
|
70
|
+
### 2. Configuring shop defaults
|
71
|
+
|
72
|
+
Before running the shop, please read through the generated Stall initializer
|
73
|
+
file at `config/initializers/stall.rb` and customize their values to your
|
74
|
+
fir your desired shop behavior.
|
75
|
+
|
76
|
+
Here are the mandatory ones :
|
77
|
+
|
78
|
+
- `store_name`
|
79
|
+
- `admin_email`
|
80
|
+
- `sender_email`
|
81
|
+
- `default_app_domain`
|
82
|
+
|
83
|
+
|
84
|
+
### 3. Configuring the checkout flow
|
62
85
|
|
63
86
|
The checkout process is completely flexible and can be overriden easily.
|
64
87
|
|
@@ -66,7 +89,22 @@ Please see the Wiki page :
|
|
66
89
|
[The checkout process](https://github.com/rails-stall/stall/wiki/The-checkout-process)
|
67
90
|
|
68
91
|
|
69
|
-
###
|
92
|
+
### 4. Customizing views
|
93
|
+
|
94
|
+
You can copy stall views to your app with the `stall:view` generator.
|
95
|
+
The less you customize the views, the more you get it to work with future
|
96
|
+
Stall versions.
|
97
|
+
|
98
|
+
Find the templates you need by browsing the source code (ex: `bundle open stall`)
|
99
|
+
and generate the needed views. You can pass any number of views at a time.
|
100
|
+
|
101
|
+
Example :
|
102
|
+
|
103
|
+
```bash
|
104
|
+
rails generate stall:view checkout/steps/_informations checkout/steps/_payment stall/carts/_cart
|
105
|
+
```
|
106
|
+
|
107
|
+
### 5. Cleaning up aborted carts
|
70
108
|
|
71
109
|
A cart is created for each new visit on the app. You may want to clean
|
72
110
|
aborted carts to avoid the table to grow too big.
|
@@ -1,11 +1,40 @@
|
|
1
1
|
class Stall.AddToCartForm extends Vertebra.View
|
2
2
|
@create = ($el) ->
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
unless (instance = $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
|
+
instance.sendRequest()
|
6
8
|
|
7
9
|
events:
|
8
10
|
'ajax:success': 'onSuccess'
|
11
|
+
'ajax:complete': 'onComplete'
|
12
|
+
'change [name$="[sellable_id]"]': 'onValidatableFieldChanged'
|
13
|
+
'change [name$="[quantity]"]': 'onValidatableFieldChanged'
|
14
|
+
|
15
|
+
initialize: ->
|
16
|
+
@$button = @$('[type="submit"]')
|
17
|
+
@errorMessages = @$el.data('error-messages')
|
18
|
+
|
19
|
+
sendRequest: ->
|
20
|
+
return false unless @validate(submit: true) and !@errors.length
|
21
|
+
@setLoading(true)
|
22
|
+
true
|
23
|
+
|
24
|
+
onValidatableFieldChanged: ->
|
25
|
+
@validate()
|
26
|
+
|
27
|
+
validate: (options = {})->
|
28
|
+
@checkErrors()
|
29
|
+
@refreshErrorsDisplay(options)
|
30
|
+
|
31
|
+
checkErrors: ->
|
32
|
+
@errors = []
|
33
|
+
@errors.push('choose') unless @$('[name$="[sellable_id]"]').val()
|
34
|
+
@errors.push('quantity') unless @$('[name$="[quantity]"]').val()
|
35
|
+
|
36
|
+
onComplete: ->
|
37
|
+
@setLoading(false)
|
9
38
|
|
10
39
|
onSuccess: (e, resp) ->
|
11
40
|
@$modal = $(resp).appendTo('body').modal()
|
@@ -17,6 +46,29 @@ class Stall.AddToCartForm extends Vertebra.View
|
|
17
46
|
if ($counter = $('[data-cart-quantity-counter]')).length
|
18
47
|
$counter.text(quantity)
|
19
48
|
|
49
|
+
# Displays errors in a tooltip on the form submit button, listing different
|
50
|
+
# errors and disabling the submit button
|
51
|
+
refreshErrorsDisplay: (options = {}) ->
|
52
|
+
buttonIsTooltip = @$button.data('bs.tooltip')
|
53
|
+
|
54
|
+
if @errors.length
|
55
|
+
messages = (@errorMessages[error] for error in @errors)
|
56
|
+
message = messages.join('<br>')
|
57
|
+
@$button.attr(title: message)
|
58
|
+
@$button.tooltip() unless buttonIsTooltip
|
59
|
+
# Force tooltip display if the user just submitted the form
|
60
|
+
@$button.tooltip('show') if options.submit
|
61
|
+
@$button.prop('disabled', true)
|
62
|
+
else
|
63
|
+
@$button.attr(title: '')
|
64
|
+
@$button.tooltip('destroy') if buttonIsTooltip
|
65
|
+
@$button.prop('disabled', false)
|
66
|
+
|
67
|
+
setLoading: (loading) ->
|
68
|
+
state = if loading then 'loading' else 'reset'
|
69
|
+
@$button.button(state)
|
70
|
+
|
71
|
+
|
20
72
|
Stall.onDomReady ->
|
21
73
|
$('body').on 'ajax:beforeSend', '[data-add-to-cart-form]', (e) ->
|
22
74
|
Stall.AddToCartForm.create($(e.currentTarget))
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Stall.AddressesFields extends Vertebra.View
|
2
|
+
events:
|
3
|
+
'change [data-use-another-address-for-billing]': 'addressesSwitchChanged'
|
4
|
+
|
5
|
+
initialize: ->
|
6
|
+
@refresh()
|
7
|
+
|
8
|
+
addressesSwitchChanged: ->
|
9
|
+
@refresh()
|
10
|
+
|
11
|
+
refresh: ->
|
12
|
+
checked = @$('[data-use-another-address-for-billing]').is(':checked')
|
13
|
+
@$('[data-address-form="billing"]').toggleClass('hidden', !checked)
|
14
|
+
|
15
|
+
Stall.onDomReady ->
|
16
|
+
if ($addressesFields = $('[data-addresses-fields]')).length
|
17
|
+
new Stall.AddressesFields(el: $addressesFields)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# This class handles the sign in form inside the checkout process, allowing the
|
2
|
+
# customers to sign in directly during the checkout, without leaving the
|
3
|
+
# process.
|
4
|
+
#
|
5
|
+
class Stall.RemoteSignInForm extends Vertebra.View
|
6
|
+
@create = ($el) ->
|
7
|
+
unless (instance = $el.data('stall.remote-sign-in-form'))
|
8
|
+
instance = new Stall.RemoteSignInForm(el: $el)
|
9
|
+
instance.onBeforeSend()
|
10
|
+
$el.data('stall.remote-sign-in-form', instance)
|
11
|
+
|
12
|
+
events:
|
13
|
+
'ajax:beforeSend': 'onBeforeSend'
|
14
|
+
'ajax:success': 'onSuccess'
|
15
|
+
'ajax:error': 'onError'
|
16
|
+
|
17
|
+
initialize: ->
|
18
|
+
# We store the current URL to allow refreshing the page after the user
|
19
|
+
# signed in, since Turbolinks 5 automatically forces the browser to follow
|
20
|
+
# server redirections by evaluating javascript in the browser. Since we
|
21
|
+
# don't want to configure Devise (or other gems) server-side, we work-around
|
22
|
+
# this issue by running a second redirection, manually, with the URL we
|
23
|
+
# store here
|
24
|
+
@currentURL = window.location.href
|
25
|
+
|
26
|
+
onBeforeSend: ->
|
27
|
+
@setLoading(true)
|
28
|
+
|
29
|
+
onSuccess: (e, response) ->
|
30
|
+
@setLoading(false)
|
31
|
+
@removeFlashMessage()
|
32
|
+
Turbolinks.visit(@currentURL)
|
33
|
+
|
34
|
+
onError: (e, jqXHR) ->
|
35
|
+
@setFlashMessage(jqXHR.responseText, type: 'danger')
|
36
|
+
@setLoading(false)
|
37
|
+
|
38
|
+
setLoading: (loading) ->
|
39
|
+
state = if loading then 'loading' else 'reset'
|
40
|
+
@$('[data-sign-in-submit-btn]').button(state)
|
41
|
+
|
42
|
+
setFlashMessage: (message, options = {}) ->
|
43
|
+
@removeFlashMessage()
|
44
|
+
alertClass = "alert alert-#{ options.type }"
|
45
|
+
$('<div/>', class: alertClass, 'data-flash': true)
|
46
|
+
.html(message).prependTo(@$el)
|
47
|
+
|
48
|
+
removeFlashMessage: ->
|
49
|
+
@$('[data-flash]').remove()
|
50
|
+
|
51
|
+
|
52
|
+
Stall.onDomReady ->
|
53
|
+
$('[data-stall-remote-sign-in-form]').each (i, el) ->
|
54
|
+
$(el).one 'ajax:beforeSend', (e) ->
|
55
|
+
Stall.RemoteSignInForm.create($(e.currentTarget))
|
@@ -3,11 +3,13 @@
|
|
3
3
|
#= require_self
|
4
4
|
#= require stall/add-to-cart-form
|
5
5
|
#= require stall/cart-form
|
6
|
+
#= require stall/addresses-fields
|
7
|
+
#= require stall/remote-sign-in-form
|
6
8
|
|
7
9
|
@Stall =
|
8
10
|
onDomReady: (callback) ->
|
9
11
|
event = if window.Turbolinks && window.Turbolinks.supported
|
10
|
-
'page:change'
|
12
|
+
'page:change turbolinks:load'
|
11
13
|
else
|
12
14
|
'ready'
|
13
15
|
|
@@ -17,7 +17,7 @@ module Stall
|
|
17
17
|
return Stall.config.default_layout if Stall.config.default_layout
|
18
18
|
|
19
19
|
parent_controller = self.class.ancestors.find do |ancestor|
|
20
|
-
!ancestor.name.match(/^Stall::/)
|
20
|
+
!ancestor.name.to_s.match(/^Stall::/) && Class === ancestor
|
21
21
|
end
|
22
22
|
|
23
23
|
parent_controller._layout ||= 'application'
|
@@ -3,6 +3,7 @@ module Stall
|
|
3
3
|
class StepsController < Stall::ApplicationController
|
4
4
|
include Stall::CheckoutHelper
|
5
5
|
|
6
|
+
skip_before_action :verify_authenticity_token, on: :foreign_update
|
6
7
|
before_action :load_cart
|
7
8
|
before_action :load_step
|
8
9
|
before_action :ensure_cart_checkoutable
|
@@ -22,6 +23,8 @@ module Stall
|
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
26
|
+
alias_method :foreign_update, :update
|
27
|
+
|
25
28
|
def change
|
26
29
|
target_step = params[:step]
|
27
30
|
|
@@ -53,7 +56,7 @@ module Stall
|
|
53
56
|
|
54
57
|
unless @cart.checkoutable?
|
55
58
|
flash[:error] = t('stall.checkout.shared.not_checkoutable')
|
56
|
-
|
59
|
+
redirect_to_referrer_or_root
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
@@ -67,12 +70,26 @@ module Stall
|
|
67
70
|
step.inject(:cookies, cookies)
|
68
71
|
step.inject(:request, request)
|
69
72
|
step.inject(:flash, flash)
|
73
|
+
step.inject(:stall_user_signed_in?, stall_user_signed_in?)
|
74
|
+
step.inject(:current_stall_user, current_stall_user)
|
70
75
|
|
71
76
|
if Stall.config.steps_initialization
|
72
77
|
instance_exec(step, &Stall.config.steps_initialization)
|
73
78
|
end
|
74
79
|
end
|
75
80
|
end
|
81
|
+
|
82
|
+
def redirect_to_referrer_or_root
|
83
|
+
referrer = URI.parse(request.referrer).path if request.referrer
|
84
|
+
|
85
|
+
redirect_target = if referrer && referrer != request.path
|
86
|
+
request.referrer
|
87
|
+
else
|
88
|
+
root_path
|
89
|
+
end
|
90
|
+
|
91
|
+
redirect_to redirect_target
|
92
|
+
end
|
76
93
|
end
|
77
94
|
end
|
78
95
|
end
|
@@ -9,14 +9,13 @@ module Stall
|
|
9
9
|
calculator_class = Stall::Shipping::Calculator.for(shipping_method)
|
10
10
|
|
11
11
|
unless calculator_class
|
12
|
-
raise Stall::Shipping::CalculatorNotFound,
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
"#{ shipping_method.identifier }`"
|
12
|
+
raise Stall::Shipping::CalculatorNotFound, <<-MSG.squish
|
13
|
+
No calculator found for the shipping method : #{ shipping_method.name }
|
14
|
+
(#{ shipping_method.identifier }).
|
15
|
+
Please remove the Stall::ShippingMethod with id #{ shipping_method.id }
|
16
|
+
or create the associated calculator with
|
17
|
+
`rails g stall:shipping:calculator #{ shipping_method.identifier }`
|
18
|
+
MSG
|
20
19
|
end
|
21
20
|
|
22
21
|
calculator = calculator_class.new(cart, shipping_method)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Stall
|
2
|
+
module CustomersHelper
|
3
|
+
def stall_user_signed_in?
|
4
|
+
!!current_stall_user
|
5
|
+
end
|
6
|
+
|
7
|
+
def current_stall_user
|
8
|
+
send(Stall.config.default_user_helper_method)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Copy e-mail error messages from user to customer, allowing them to be
|
12
|
+
# displayed in customer e-mail input to the visitor
|
13
|
+
#
|
14
|
+
def with_errors_from_user(customer)
|
15
|
+
return customer unless (user = customer.user) && user.errors.any?
|
16
|
+
return unless (messages = user.errors.messages[:email]) && messages.any?
|
17
|
+
|
18
|
+
messages.each do |message|
|
19
|
+
customer.errors.add(:email, message)
|
20
|
+
end
|
21
|
+
|
22
|
+
customer
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -12,6 +12,26 @@ module Stall
|
|
12
12
|
accepts_nested_attributes_for :user
|
13
13
|
|
14
14
|
has_many :product_lists, dependent: :destroy
|
15
|
+
|
16
|
+
validates :email, format: { with: /\A[^@\s]+@([^@\s]+\.)+[^@\W]+\z/ },
|
17
|
+
allow_blank: true
|
18
|
+
|
19
|
+
before_validation :ensure_user_email
|
20
|
+
|
21
|
+
def user_or_default
|
22
|
+
user || build_user
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_user(attributes = {})
|
26
|
+
attributes.reverse_merge!(customer: self)
|
27
|
+
self.user = Stall.config.default_user_model.new(attributes)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def ensure_user_email
|
33
|
+
user.email = email if user && user.email.blank?
|
34
|
+
end
|
15
35
|
end
|
16
36
|
end
|
17
37
|
end
|
@@ -21,6 +21,7 @@ module Stall
|
|
21
21
|
after_initialize :ensure_currency
|
22
22
|
after_initialize :ensure_state
|
23
23
|
|
24
|
+
before_save :save_customer_if_changed
|
24
25
|
after_save :ensure_reference, on: :create
|
25
26
|
|
26
27
|
scope :empty, -> {
|
@@ -90,6 +91,14 @@ module Stall
|
|
90
91
|
true
|
91
92
|
end
|
92
93
|
|
94
|
+
def currency
|
95
|
+
@currency ||= if (currency = read_attribute(:currency).presence)
|
96
|
+
Money::Currency.new(currency)
|
97
|
+
else
|
98
|
+
self.currency = Money.default_currency
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
93
102
|
private
|
94
103
|
|
95
104
|
def ensure_currency
|
@@ -112,6 +121,10 @@ module Stall
|
|
112
121
|
end
|
113
122
|
end
|
114
123
|
|
124
|
+
def save_customer_if_changed
|
125
|
+
customer.save if customer && customer.changed?
|
126
|
+
end
|
127
|
+
|
115
128
|
module ClassMethods
|
116
129
|
def find_by_reference(reference)
|
117
130
|
where("data->>'reference' = ?", reference).first
|
@@ -1,41 +1,30 @@
|
|
1
|
+
-# Display sign in form for users that are not signed in
|
2
|
+
- unless stall_user_signed_in?
|
3
|
+
- resource = Stall.config.default_user_model.new
|
4
|
+
= render partial: 'stall/customers/sign_in', locals: { resource: resource, resource_name: resource.model_name.element }
|
5
|
+
|
1
6
|
%h1= t('stall.checkout.informations.title')
|
2
7
|
|
3
8
|
= simple_form_for cart, as: :cart, url: step_path do |form|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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, collection: Address.civility_enum
|
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]
|
9
|
+
.row
|
10
|
+
.col-md-7
|
11
|
+
= render partial: 'stall/customers/fields', locals: { form: form, cart: cart }
|
12
|
+
= render partial: 'stall/addresses/fields', locals: { form: form, cart: cart }
|
24
13
|
|
25
|
-
|
26
|
-
|
27
|
-
=
|
14
|
+
.col-md-5
|
15
|
+
= render partial: 'stall/carts/cart', locals: { cart: cart }
|
16
|
+
= render partial: 'stall/shipments/fields', locals: { form: form, cart: cart }
|
17
|
+
= render partial: 'stall/payments/fields', locals: { form: form, cart: cart }
|
28
18
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
=
|
35
|
-
=
|
36
|
-
=
|
37
|
-
|
38
|
-
= address_form.input :phone
|
19
|
+
-# Basic implementation to display the terms acceptance checkbox before
|
20
|
+
-# the order is placed. Just fill in the `config.terms_path` in the
|
21
|
+
-# Stall initializer if you want to change the terms URL, but feel free
|
22
|
+
-# to customize further if needed.
|
23
|
+
= form.input :terms, label: false do
|
24
|
+
= form.check_box :terms
|
25
|
+
= form.label :terms, t('stall.checkout.informations.accept_the')
|
26
|
+
= link_to Stall.config.terms_path, target: '_blank' do
|
27
|
+
= form.object.class.human_attribute_name(:terms)
|
39
28
|
|
40
|
-
|
41
|
-
|
29
|
+
%button.btn.btn-primary.btn-lg.btn-block{ type: 'submit' }
|
30
|
+
= t('stall.checkout.informations.validate')
|
@@ -1,49 +1,5 @@
|
|
1
1
|
%h1= t('stall.checkout.payment.title')
|
2
2
|
|
3
|
-
|
4
|
-
%thead
|
5
|
-
%tr
|
6
|
-
%th= LineItem.human_attribute_name(:name)
|
7
|
-
%th= LineItem.human_attribute_name(:unit_price)
|
8
|
-
%th= LineItem.human_attribute_name(:quantity)
|
9
|
-
%th= LineItem.human_attribute_name(:price)
|
10
|
-
%tbody
|
11
|
-
- cart.line_items.each do |line_item|
|
12
|
-
%tr{ data: { :'line-item-id' => line_item.id } }
|
13
|
-
%td= line_item.name
|
14
|
-
%td= number_to_currency(line_item.unit_price)
|
15
|
-
%td= line_item.quantity
|
16
|
-
%td= number_to_currency(line_item.price)
|
17
|
-
|
18
|
-
- if cart.subtotal != cart.total_price
|
19
|
-
%tr
|
20
|
-
%td{ colspan: 3 }= t('stall.carts.recap.subtotal')
|
21
|
-
%td= number_to_currency(cart.subtotal)
|
22
|
-
|
23
|
-
- if cart.shipment && cart.shipment
|
24
|
-
%tr
|
25
|
-
%td{ colspan: 3 }
|
26
|
-
= t('stall.carts.recap.total_shipment_price')
|
27
|
-
\:
|
28
|
-
= cart.shipment.shipping_method.name
|
29
|
-
|
30
|
-
%td= number_to_currency(cart.shipment.price)
|
31
|
-
|
32
|
-
- cart.adjustments.each do |adjustment|
|
33
|
-
%tr
|
34
|
-
%td{ colspan: 3 }= adjustment.name
|
35
|
-
%td= number_to_currency(adjustment.price)
|
36
|
-
|
37
|
-
%tr
|
38
|
-
%td{ colspan: 3 }= t('stall.carts.recap.total_eot_price')
|
39
|
-
%td= number_to_currency(cart.total_eot_price)
|
40
|
-
|
41
|
-
%tr
|
42
|
-
%td{ colspan: 3 }= t('stall.carts.recap.total_vat')
|
43
|
-
%td= number_to_currency(cart.total_vat)
|
44
|
-
|
45
|
-
%tr
|
46
|
-
%td{ colspan: 3 }= t('stall.carts.recap.total_price')
|
47
|
-
%td= number_to_currency(cart.total_price)
|
3
|
+
= render partial: 'stall/carts/cart', locals: { cart: cart }
|
48
4
|
|
49
5
|
= payment_button_for(cart)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
.addresses-fields{ data: { :'addresses-fields' => true } }
|
2
|
+
= form.fields_for :address_ownerships, [cart.address_ownership_for(:shipping)] do |ownership_form|
|
3
|
+
%fieldset{ data: { :'address-form' => :shipping } }
|
4
|
+
= ownership_form.hidden_field :shipping
|
5
|
+
|
6
|
+
= ownership_form.fields_for :address do |address_form|
|
7
|
+
= render partial: 'stall/addresses/nested_fields', locals: { form: address_form }
|
8
|
+
|
9
|
+
= form.input :use_another_address_for_billing, as: :boolean, input_html: { data: { :'use-another-address-for-billing' => true } }
|
10
|
+
|
11
|
+
= form.fields_for :address_ownerships, [cart.address_ownership_for(:billing)] do |ownership_form|
|
12
|
+
%fieldset{ class: ('hidden' unless params[:use_another_address_for_billing] == '1'), data: { :'address-form' => :billing } }
|
13
|
+
= ownership_form.hidden_field :billing
|
14
|
+
|
15
|
+
= ownership_form.fields_for :address do |address_form|
|
16
|
+
= render partial: 'stall/addresses/nested_fields', locals: { form: address_form }
|
@@ -0,0 +1,14 @@
|
|
1
|
+
.row
|
2
|
+
.col-md-2= form.input :civility, collection: Address.civility_enum
|
3
|
+
.col-md-5= form.input :first_name
|
4
|
+
.col-md-5= form.input :last_name
|
5
|
+
.row
|
6
|
+
.col-xs-12= form.input :address, input_html: { rows: 1 }
|
7
|
+
.row
|
8
|
+
.col-xs-12= form.input :address_details, label: false, input_html: { rows: 1 }
|
9
|
+
.row
|
10
|
+
.col-md-4= form.input :country
|
11
|
+
.col-md-4= form.input :zip
|
12
|
+
.col-md-4= form.input :city
|
13
|
+
.row
|
14
|
+
.col-xs-12= form.input :phone
|
@@ -0,0 +1,45 @@
|
|
1
|
+
%table.table.table-hover
|
2
|
+
%thead
|
3
|
+
%tr
|
4
|
+
%th= LineItem.human_attribute_name(:name)
|
5
|
+
%th= LineItem.human_attribute_name(:price)
|
6
|
+
%tbody
|
7
|
+
- cart.line_items.each do |line_item|
|
8
|
+
%tr{ data: { :'line-item-id' => line_item.id } }
|
9
|
+
%td
|
10
|
+
= line_item.name
|
11
|
+
x
|
12
|
+
= line_item.quantity
|
13
|
+
%td= number_to_currency(line_item.price)
|
14
|
+
|
15
|
+
%tfoot
|
16
|
+
- if cart.subtotal != cart.total_price
|
17
|
+
%tr
|
18
|
+
%td= t('stall.carts.recap.subtotal')
|
19
|
+
%td= number_to_currency(cart.subtotal)
|
20
|
+
|
21
|
+
- if cart.shipment.try(:persisted?)
|
22
|
+
%tr
|
23
|
+
%td
|
24
|
+
= t('stall.carts.recap.total_shipment_price')
|
25
|
+
\:
|
26
|
+
= cart.shipment.shipping_method.name
|
27
|
+
|
28
|
+
%td= number_to_currency(cart.shipment.price)
|
29
|
+
|
30
|
+
- cart.adjustments.each do |adjustment|
|
31
|
+
%tr
|
32
|
+
%td= adjustment.name
|
33
|
+
%td= number_to_currency(adjustment.price)
|
34
|
+
|
35
|
+
%tr
|
36
|
+
%td= t('stall.carts.recap.total_eot_price')
|
37
|
+
%td= number_to_currency(cart.total_eot_price)
|
38
|
+
|
39
|
+
%tr
|
40
|
+
%td= t('stall.carts.recap.total_vat')
|
41
|
+
%td= number_to_currency(cart.total_vat)
|
42
|
+
|
43
|
+
%tr
|
44
|
+
%td= t('stall.carts.recap.total_price')
|
45
|
+
%td= number_to_currency(cart.total_price)
|
@@ -11,12 +11,12 @@
|
|
11
11
|
|
12
12
|
%tbody
|
13
13
|
= form.fields_for :line_items do |line_item_fields|
|
14
|
-
%tr{ data: { :'line-item-id' => line_item_fields.object.id } }
|
14
|
+
%tr.line-item-row{ data: { :'line-item-id' => line_item_fields.object.id } }
|
15
15
|
%td= line_item_fields.object.name
|
16
16
|
%td= line_item_fields.object.unit_price
|
17
17
|
%td= line_item_fields.input_field :quantity, spinner: false, class: 'form-control', data: { :'quantity-field' => true }
|
18
18
|
%td= line_item_fields.object.price
|
19
|
-
%td= link_to_remove_association '×'.html_safe, line_item_fields
|
19
|
+
%td= link_to_remove_association '×'.html_safe, line_item_fields, wrapper_class: 'line-item-row'
|
20
20
|
|
21
21
|
- if @cart.subtotal != @cart.total_price
|
22
22
|
%tr
|