stall 0.1.3 → 0.2.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 +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
|