stall 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -3
  3. data/app/assets/javascripts/stall/add-to-cart-form.coffee +55 -3
  4. data/app/assets/javascripts/stall/addresses-fields.coffee +17 -0
  5. data/app/assets/javascripts/stall/remote-sign-in-form.coffee +55 -0
  6. data/app/assets/javascripts/stall.coffee +3 -1
  7. data/app/controllers/stall/application_controller.rb +1 -1
  8. data/app/controllers/stall/carts_controller.rb +1 -3
  9. data/app/controllers/stall/checkout/steps_controller.rb +18 -1
  10. data/app/helpers/stall/checkout_helper.rb +7 -8
  11. data/app/helpers/stall/customers_helper.rb +25 -0
  12. data/app/models/stall/models/cart.rb +2 -0
  13. data/app/models/stall/models/customer.rb +20 -0
  14. data/app/models/stall/models/product_list.rb +13 -0
  15. data/app/views/checkout/steps/_informations.html.haml +24 -35
  16. data/app/views/checkout/steps/_payment.html.haml +1 -45
  17. data/app/views/checkout/steps/_shipping_method.html.haml +0 -1
  18. data/app/views/stall/addresses/_fields.html.haml +16 -0
  19. data/app/views/stall/addresses/_nested_fields.html.haml +14 -0
  20. data/app/views/stall/carts/_cart.html.haml +45 -0
  21. data/app/views/stall/carts/show.html.haml +2 -2
  22. data/app/views/stall/customers/_fields.html.haml +32 -0
  23. data/app/views/stall/customers/_sign_in.html.haml +20 -0
  24. data/app/views/stall/line_items/_form.html.haml +2 -2
  25. data/app/views/stall/payments/_fields.html.haml +2 -0
  26. data/app/views/stall/shared/mailers/_cart.html.haml +31 -29
  27. data/app/views/stall/shipments/_fields.html.haml +2 -0
  28. data/config/locales/stall.fr.yml +35 -8
  29. data/lib/generators/stall/checkout/step/templates/step.rb.erb +9 -0
  30. data/lib/generators/stall/checkout/wizard/templates/wizard.rb.erb +1 -1
  31. data/lib/generators/stall/install/install_generator.rb +6 -0
  32. data/lib/generators/stall/install/templates/initializer.rb +10 -1
  33. data/lib/generators/stall/view/view_generator.rb +38 -0
  34. data/lib/stall/addressable.rb +2 -0
  35. data/lib/stall/addresses/copier_base.rb +24 -0
  36. data/lib/stall/addresses/copy_source_to_target.rb +45 -0
  37. data/lib/stall/addresses/prefill_target_from_source.rb +29 -0
  38. data/lib/stall/addresses.rb +9 -0
  39. data/lib/stall/cart_helper.rb +14 -1
  40. data/lib/stall/checkout/informations_checkout_step.rb +133 -14
  41. data/lib/stall/checkout/payment_checkout_step.rb +21 -1
  42. data/lib/stall/checkout/step.rb +22 -15
  43. data/lib/stall/checkout/step_form.rb +22 -8
  44. data/lib/stall/config.rb +17 -3
  45. data/lib/stall/engine.rb +6 -0
  46. data/lib/stall/payable.rb +9 -0
  47. data/lib/stall/payments/gateway.rb +16 -6
  48. data/lib/stall/payments/urls_config.rb +2 -2
  49. data/lib/stall/rails/routing_mapper.rb +4 -6
  50. data/lib/stall/routes.rb +4 -1
  51. data/lib/stall/sellable.rb +13 -3
  52. data/lib/stall/version.rb +1 -1
  53. data/lib/stall.rb +1 -0
  54. metadata +25 -6
  55. data/lib/stall/checkout/payment_method_checkout_step.rb +0 -9
  56. 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: 93101cd6c71bd164e54d7f7a96009894eaee815d
4
- data.tar.gz: e9e552e25b8fa6a49ac0423cacac158b267585ab
3
+ metadata.gz: 282c715e0c37f5b8581b410bcc70a30777973284
4
+ data.tar.gz: cc5283aa96ce4dd6cbb89678fc1ef73a812be26d
5
5
  SHA512:
6
- metadata.gz: ad0c5c29e895dcadf8aff1631374685f209ce73db2e6c28ff313648e33c67093dbb0fbef762d34abcaaf9b3eb4f015a6a151285b47f8327fb194c3e162f0dfa1
7
- data.tar.gz: ad331dadf79752be6c13933ef96060aa07c4b7ae5ebbea33f42757d2160bfb10825d30c2f6dbe0092d91edb1df82abbb419bc8e55f3b5583274bd889a1b3e7ad
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
- ### Making a model sellable
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 the checkout flow
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
- ### Cleaning up aborted carts
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
- 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)
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'
@@ -40,9 +40,7 @@ module Stall
40
40
 
41
41
  def cart_params
42
42
  params.require(:cart).permit(
43
- line_items_attributes: [
44
- :id, :quantity, :_destroy
45
- ]
43
+ line_items_attributes: [:id, :quantity, :_destroy]
46
44
  )
47
45
  end
48
46
  end
@@ -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
- redirect_to request.referrer || root_path
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
- "No calculator found for the shipping method : " +
14
- "#{ shipping_method.name } " +
15
- "(#{ shipping_method.identifier }). " +
16
- "Please remove the Stall::ShippingMethod with id " +
17
- "#{ shipping_method.id } or create the associated " +
18
- "calculator with `rails g stall:shipping:calculator " +
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,8 @@ module Stall
12
12
 
13
13
  has_many :adjustments, dependent: :destroy, inverse_of: :cart
14
14
  accepts_nested_attributes_for :adjustments
15
+
16
+ attr_accessor :terms
15
17
  end
16
18
 
17
19
  def total_weight
@@ -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
- = 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, 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
- = 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
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
- = ownership_form.fields_for :address do |address_form|
30
- = address_form.input :civility, collection: Address.civility_enum
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
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
- %button.btn.btn-primary{ type: 'submit' }
41
- = t('stall.checkout.informations.validate')
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
- %table.table.table-striped
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)
@@ -7,4 +7,3 @@
7
7
  .form-actions
8
8
  %button.btn.btn-primary{ type: 'submit' }
9
9
  = t('stall.checkout.shipping_method.validate')
10
-
@@ -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 '&times;'.html_safe, line_item_fields
19
+ %td= link_to_remove_association '&times;'.html_safe, line_item_fields, wrapper_class: 'line-item-row'
20
20
 
21
21
  - if @cart.subtotal != @cart.total_price
22
22
  %tr