stall 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -23
  3. data/app/assets/javascripts/para/stall.coffee +1 -0
  4. data/app/assets/javascripts/para/stall/inputs/variant-select.coffee +62 -0
  5. data/app/assets/javascripts/para/stall/inputs/variants-matrix.coffee +12 -0
  6. data/app/assets/javascripts/para/stall/inputs/variants-matrix/helpers.coffee +40 -0
  7. data/app/assets/javascripts/para/stall/inputs/variants-matrix/input.coffee +133 -0
  8. data/app/assets/javascripts/para/stall/inputs/variants-matrix/nested-fields.coffee +38 -0
  9. data/app/assets/javascripts/para/stall/inputs/variants-matrix/properties_select.coffee +45 -0
  10. data/app/assets/javascripts/para/stall/inputs/variants-matrix/variant.coffee +59 -0
  11. data/app/assets/javascripts/stall.coffee +1 -0
  12. data/app/assets/javascripts/stall/add-to-cart-form.coffee +53 -28
  13. data/app/assets/javascripts/stall/cart-form.coffee +7 -2
  14. data/app/assets/stylesheets/para/stall.sass +28 -0
  15. data/app/controllers/para/stall/admin/carts_controller.rb +27 -0
  16. data/app/controllers/stall/cart_credits_controller.rb +27 -0
  17. data/app/controllers/stall/carts_controller.rb +1 -1
  18. data/app/controllers/stall/checkout/steps_controller.rb +22 -12
  19. data/app/controllers/stall/checkouts_controller.rb +1 -0
  20. data/app/controllers/stall/line_items_controller.rb +1 -0
  21. data/app/controllers/stall/payments_controller.rb +16 -1
  22. data/app/helpers/stall/credit_notes_helper.rb +25 -0
  23. data/app/helpers/stall/customers_helper.rb +3 -1
  24. data/app/models/billing_address.rb +2 -0
  25. data/app/models/cart_credit_note_adjustment.rb +3 -0
  26. data/app/models/credit_note.rb +3 -0
  27. data/app/models/credit_note_adjustment.rb +3 -0
  28. data/app/models/credit_note_usage.rb +3 -0
  29. data/app/models/product.rb +3 -0
  30. data/app/models/product_category.rb +3 -0
  31. data/app/models/product_detail.rb +3 -0
  32. data/app/models/property.rb +3 -0
  33. data/app/models/property_value.rb +3 -0
  34. data/app/models/shipping_address.rb +2 -0
  35. data/app/models/stall/models/address.rb +2 -2
  36. data/app/models/stall/models/cart.rb +2 -15
  37. data/app/models/stall/models/cart_credit_note_adjustment.rb +11 -0
  38. data/app/models/stall/models/credit_note.rb +50 -0
  39. data/app/models/stall/models/credit_note_adjustment.rb +13 -0
  40. data/app/models/stall/models/credit_note_usage.rb +16 -0
  41. data/app/models/stall/models/customer.rb +20 -8
  42. data/app/models/stall/models/line_item.rb +9 -0
  43. data/app/models/stall/models/product.rb +45 -0
  44. data/app/models/stall/models/product_category.rb +31 -0
  45. data/app/models/stall/models/product_detail.rb +17 -0
  46. data/app/models/stall/models/product_list.rb +9 -45
  47. data/app/models/stall/models/property.rb +20 -0
  48. data/app/models/stall/models/property_value.rb +23 -0
  49. data/app/models/stall/models/variant.rb +34 -0
  50. data/app/models/stall/models/variant_property_value.rb +18 -0
  51. data/app/models/variant.rb +3 -0
  52. data/app/models/variant_property_value.rb +3 -0
  53. data/app/services/stall/cart_credit_note_creation_service.rb +40 -0
  54. data/app/services/stall/cart_payment_validation_service.rb +28 -0
  55. data/app/services/stall/cart_update_service.rb +17 -6
  56. data/app/services/stall/credit_usage_service.rb +102 -0
  57. data/app/services/stall/payment_notification_service.rb +4 -8
  58. data/app/services/stall/product_list_staleness_handling_service.rb +33 -0
  59. data/app/views/admin/addresses/_fields.html.haml +9 -0
  60. data/app/views/admin/carts/_filters.html.haml +17 -0
  61. data/app/views/admin/carts/_form.html.haml +43 -0
  62. data/app/views/admin/carts/_table.html.haml +17 -0
  63. data/app/views/admin/customers/_fields.html.haml +1 -0
  64. data/app/views/admin/line_items/_fields.html.haml +15 -0
  65. data/app/views/admin/products/_table.html.haml +12 -0
  66. data/app/views/admin/properties/_form.html.haml +6 -0
  67. data/app/views/admin/properties/_table.html.haml +8 -0
  68. data/app/views/admin/property_values/_fields.html.haml +1 -0
  69. data/app/views/admin/shipments/_fields.html.haml +7 -0
  70. data/app/views/checkout/steps/_informations.html.haml +3 -1
  71. data/app/views/checkout/steps/_payment.html.haml +2 -0
  72. data/app/views/checkout/steps/_payment_return.html.haml +0 -1
  73. data/app/views/para/admin/resources/_variant_row.html.haml +24 -0
  74. data/app/views/para/stall/inputs/_variant_select.html.haml +14 -0
  75. data/app/views/para/stall/inputs/_variants_matrix.html.haml +41 -0
  76. data/app/views/stall/addresses/_fields.html.haml +6 -12
  77. data/app/views/stall/carts/_cart.html.haml +45 -37
  78. data/app/views/stall/carts/_widget.html.haml +28 -0
  79. data/app/views/stall/carts/show.html.haml +2 -0
  80. data/app/views/stall/checkout/steps/_navigation.html.haml +13 -0
  81. data/app/views/stall/credit_note_adjustments/_form.html.haml +28 -0
  82. data/app/views/stall/line_items/_added.html.haml +2 -2
  83. data/app/views/stall/line_items/_form.html.haml +1 -1
  84. data/app/views/stall/payments/manual_payment_gateway/_form.html.haml +10 -0
  85. data/app/views/stall/shared/mailers/_cart.html.haml +1 -1
  86. data/config/locales/stall.fr.yml +82 -2
  87. data/db/migrate/20161129101956_add_type_to_stall_address_ownerships.rb +52 -0
  88. data/db/migrate/20161202080218_add_reference_to_product_lists.rb +17 -0
  89. data/db/migrate/20170118103916_create_credit_notes.rb +17 -0
  90. data/db/migrate/20170118144047_create_credit_note_adjustments.rb +13 -0
  91. data/db/migrate/20170123123115_create_stall_product_categories.rb +12 -0
  92. data/db/migrate/20170123123326_create_stall_products.rb +17 -0
  93. data/db/migrate/20170123125030_create_stall_variants.rb +13 -0
  94. data/db/migrate/20170123131748_create_stall_product_category_hierarchies.rb +16 -0
  95. data/db/migrate/20170123143704_create_stall_product_details.rb +14 -0
  96. data/db/migrate/20170125152622_convert_all_money_fields_to_decimal_to_use_infinite_precision.rb +27 -0
  97. data/db/migrate/20170131162537_add_data_to_stall_adjustments.rb +5 -0
  98. data/db/migrate/20170202165514_create_stall_properties.rb +9 -0
  99. data/db/migrate/20170202165516_create_stall_property_values.rb +13 -0
  100. data/db/migrate/20170202165518_create_stall_variant_property_values.rb +13 -0
  101. data/lib/generators/stall/install/templates/initializer.rb +21 -0
  102. data/lib/generators/stall/view/view_generator.rb +41 -19
  103. data/lib/para/stall.rb +32 -0
  104. data/lib/para/stall/inputs.rb +13 -0
  105. data/lib/para/stall/inputs/variant_input_helper.rb +34 -0
  106. data/lib/para/stall/inputs/variant_select_input.rb +79 -0
  107. data/lib/para/stall/inputs/variants_matrix_input.rb +72 -0
  108. data/lib/para/stall/routes.rb +11 -0
  109. data/lib/para/stall/variants_property_config.rb +78 -0
  110. data/lib/stall.rb +10 -0
  111. data/lib/stall/addressable.rb +11 -59
  112. data/lib/stall/addresses.rb +1 -0
  113. data/lib/stall/addresses/copier_base.rb +3 -1
  114. data/lib/stall/addresses/copy.rb +10 -0
  115. data/lib/stall/addresses/copy_source_to_target.rb +10 -24
  116. data/lib/stall/addresses/prefill_target_from_source.rb +8 -16
  117. data/lib/stall/adjustable.rb +20 -0
  118. data/lib/stall/archived_paid_cart_helper.rb +36 -0
  119. data/lib/stall/cart_helper.rb +15 -7
  120. data/lib/stall/checkout/informations_checkout_step.rb +47 -50
  121. data/lib/stall/checkout/payment_return_checkout_step.rb +4 -1
  122. data/lib/stall/checkout/step.rb +24 -3
  123. data/lib/stall/checkout/step_form.rb +11 -5
  124. data/lib/stall/checkout/wizard.rb +7 -6
  125. data/lib/stall/config.rb +9 -0
  126. data/lib/stall/default_currency_manager.rb +27 -0
  127. data/lib/stall/engine.rb +14 -3
  128. data/lib/stall/payments.rb +2 -0
  129. data/lib/stall/payments/gateway_request.rb +15 -0
  130. data/lib/stall/payments/gateway_response.rb +33 -0
  131. data/lib/stall/payments/manual_payment_gateway.rb +86 -0
  132. data/lib/stall/priceable.rb +4 -0
  133. data/lib/stall/reference_manager.rb +17 -0
  134. data/lib/stall/routes.rb +1 -0
  135. data/lib/stall/shippable.rb +18 -0
  136. data/lib/stall/total_prices_manager.rb +40 -0
  137. data/lib/stall/version.rb +1 -1
  138. metadata +120 -5
  139. data/app/models/address_ownership.rb +0 -3
  140. data/app/models/stall/models/address_ownership.rb +0 -26
@@ -0,0 +1,59 @@
1
+ class VariantsMatrix.Variant extends Vertebra.View
2
+ events:
3
+ 'change [data-variants-matrix-variant-enabled]': 'onEnabledStateChanged'
4
+
5
+ initialize: (options = {}) ->
6
+ @combination = options.combination
7
+ @persisted = @$el?.data('variant-id')
8
+ @input = options.input
9
+
10
+ renderTo: ($container) ->
11
+ $variant = $(@input.nestedFieldsManager.render())
12
+ $variant.appendTo($container)
13
+ @setElement($variant)
14
+ @$el.simpleForm()
15
+ @fillProperties()
16
+
17
+ fillProperties: ->
18
+ for propertyValue in @propertyValues()
19
+ $propertyValue = @$("[data-variants-matrix-variant-property='#{ propertyValue.propertyId }']")
20
+ $propertyValue.find('[data-property-name]').html(propertyValue.name)
21
+ $propertyValue.find('[data-property-value-id]').val(propertyValue.id)
22
+ $propertyValue.removeClass('hidden')
23
+
24
+ remove: ->
25
+ if @persisted
26
+ @$el.hide(0)
27
+ @setDestroyed(true)
28
+ else
29
+ @$el.remove()
30
+ @trigger('destroy', this)
31
+
32
+ show: ->
33
+ @$el.show(0)
34
+ @setDestroyed(false)
35
+
36
+ matches: (combination) ->
37
+ VariantsMatrix.objectsAreEqual(@combination, combination)
38
+
39
+ propertyValues: ->
40
+ @_propertyValues ?= for key, value of @combination when @combination.hasOwnProperty(key)
41
+ @buildPropertyValueFor(key, value)
42
+
43
+ buildPropertyValueFor: (key, value) ->
44
+ id: value.id
45
+ name: value.name
46
+ type: key
47
+ propertyId: value.propertyId
48
+
49
+ setEnabledState: (state) ->
50
+ @$('[data-variants-matrix-variant-enabled]')
51
+ .prop('checked', state)
52
+ .trigger('change')
53
+
54
+ onEnabledStateChanged: (e) ->
55
+ checked = @$('[data-variants-matrix-variant-enabled]').prop('checked')
56
+ @$el.toggleClass('disabled')
57
+
58
+ setDestroyed: (state) ->
59
+ @$el.find('[data-variant-remove]').val(if state then 'true' else 'false')
@@ -6,6 +6,7 @@
6
6
  #= require stall/addresses-fields
7
7
  #= require stall/remote-sign-in-form
8
8
 
9
+
9
10
  @Stall =
10
11
  onDomReady: (callback) ->
11
12
  event = if window.Turbolinks && window.Turbolinks.supported
@@ -1,10 +1,18 @@
1
1
  class Stall.AddToCartForm extends Vertebra.View
2
2
  @create = ($el) ->
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)
3
+ unless (form = $el.data('stall.add-to-cart-form'))
4
+ form = new Stall.AddToCartForm(el: $el)
5
+ $el.data('stall.add-to-cart-form', form)
6
6
 
7
- instance.sendRequest()
7
+ form
8
+
9
+ @validate = ($el) ->
10
+ form = Stall.AddToCartForm.create($el)
11
+ form.validate()
12
+
13
+ @sendRequest = ($el) ->
14
+ form = Stall.AddToCartForm.create($el)
15
+ form.sendRequest()
8
16
 
9
17
  events:
10
18
  'ajax:success': 'onSuccess'
@@ -17,7 +25,9 @@ class Stall.AddToCartForm extends Vertebra.View
17
25
  @errorMessages = @$el.data('error-messages')
18
26
 
19
27
  sendRequest: ->
20
- return false unless @validate(submit: true) and !@errors.length
28
+ unless (v = @validate(submit: true)) and (e = !@errors.length)
29
+ console.log 'sendRequest', v, e
30
+ return false
21
31
  @setLoading(true)
22
32
  true
23
33
 
@@ -27,42 +37,54 @@ class Stall.AddToCartForm extends Vertebra.View
27
37
  validate: (options = {})->
28
38
  @checkErrors()
29
39
  @refreshErrorsDisplay(options)
40
+ !@errors.length
30
41
 
31
42
  checkErrors: ->
32
43
  @errors = []
33
- @errors.push('choose') unless @$('[name$="[sellable_id]"]').val()
34
- @errors.push('quantity') unless @$('[name$="[quantity]"]').val()
44
+ @errors.push('choose') unless @sellableChosen()
45
+ @errors.push('quantity') unless @quantityFilled()
46
+
47
+ sellableChosen: ->
48
+ !!@$('[name$="[sellable_id]"]').val()
49
+
50
+ quantityFilled: ->
51
+ quantity = parseInt(@$('[name$="[quantity]"]').val(), 10)
52
+ quantity > 0
35
53
 
36
54
  onComplete: ->
37
55
  @setLoading(false)
38
56
 
39
57
  onSuccess: (e, resp) ->
40
58
  @$modal = $(resp).appendTo('body').modal()
41
- @updateTotalQuantityCounter()
42
-
43
- updateTotalQuantityCounter: ->
44
- quantity = @$modal.data('cart-total-quantity')
59
+ @updateWidget()
45
60
 
46
- if ($counter = $('[data-cart-quantity-counter]')).length
47
- $counter.text(quantity)
61
+ updateWidget: ->
62
+ if ($widget = $('[data-cart-widget]'))
63
+ $widget.replaceWith(@$modal.data('cart-widget-markup'))
64
+ else if ($counter = $('[data-cart-quantity-counter]')).length
65
+ $counter.text(@$modal.data('cart-total-quantity'))
48
66
 
49
67
  # Displays errors in a tooltip on the form submit button, listing different
50
68
  # errors and disabling the submit button
51
69
  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)
70
+ @clearErrorMessages()
71
+ @displayErrorMessages(options) if @errors.length
72
+
73
+ displayErrorMessages: (options) ->
74
+ messages = (@errorMessages[error] for error in @errors)
75
+ message = messages.join('<br>')
76
+ @$button.attr('data-original-title': message)
77
+ @$button.tooltip(html: true)
78
+ @$button.tooltip('enable')
79
+ # Force tooltip display if the user just submitted the form
80
+ @$button.tooltip('show') if options.submit
81
+ @$button.prop('disabled', true)
82
+
83
+ clearErrorMessages: ->
84
+ @$button.attr(title: '')
85
+ @$button.tooltip('disable') if @$button.data('bs.tooltip')
86
+ @$button.prop('disabled', false)
87
+
66
88
 
67
89
  setLoading: (loading) ->
68
90
  state = if loading then 'loading' else 'reset'
@@ -70,5 +92,8 @@ class Stall.AddToCartForm extends Vertebra.View
70
92
 
71
93
 
72
94
  Stall.onDomReady ->
95
+ $('[data-add-to-cart-form]').each (i, el) ->
96
+ Stall.AddToCartForm.validate($(el))
97
+
73
98
  $('body').on 'ajax:beforeSend', '[data-add-to-cart-form]', (e) ->
74
- Stall.AddToCartForm.create($(e.currentTarget))
99
+ Stall.AddToCartForm.sendRequest($(e.currentTarget))
@@ -14,10 +14,15 @@ class Stall.CartForm extends Vertebra.View
14
14
  @$el.submit()
15
15
 
16
16
  updateSuccess: (e, resp) ->
17
- $form = $(resp).find('[data-cart-form]')
17
+ @updateCartFormWith(resp)
18
+
19
+ updateCartFormWith: (markup) =>
20
+ $form = $(markup).find('[data-cart-form]')
18
21
  @$el.html($form.html())
19
22
  @clean()
20
23
 
24
+
21
25
  Stall.onDomReady ->
22
26
  if ($cartForm = $('[data-cart-form]')).length
23
- new Stall.CartForm(el: $cartForm)
27
+ cartForm = new Stall.CartForm(el: $cartForm)
28
+ $cartForm.data('stall.cart-form', cartForm)
@@ -0,0 +1,28 @@
1
+ .variants-matrix-variant-row
2
+ &.disabled
3
+ text-decoration: line-through
4
+ opacity: 0.5
5
+
6
+ .variants-matrix-properties-select
7
+ > .help-block
8
+ margin: 0 0 10px
9
+
10
+ > .dropdown
11
+ margin-bottom: 15px
12
+
13
+ .variants-matrix-property-selector
14
+ padding: 5px
15
+
16
+ .variants-matrix-property-selector-label
17
+ display: block
18
+ padding: 5px
19
+
20
+ .form-group.variants_matrix
21
+ padding-right: 0
22
+
23
+ > .col-sm-9
24
+ width: 100%
25
+ float: none
26
+ padding-top: 0
27
+ padding-bottom: 0
28
+ margin-bottom: -1px
@@ -0,0 +1,27 @@
1
+ module Para
2
+ module Stall
3
+ module Admin
4
+ class CartsController < ::Para::Admin::CrudResourcesController
5
+ def index
6
+ # Default to only showing paid carts
7
+ params[:q] ||= {}
8
+ params[:q][:payment_paid_at_null] ||= 0
9
+
10
+ super
11
+
12
+ # Only show filled carts, forget empty ones that just wait to be
13
+ # cleaned up by the rake task
14
+ @resources = @resources.filled
15
+ end
16
+
17
+ private
18
+
19
+ def shipment_params
20
+ params.require(:shipment).permit(
21
+ :carrier, :tracking_code, :point_of_sale_id
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Stall
2
+ class CartCreditsController < Stall::ApplicationController
3
+ before_action :load_cart
4
+
5
+ def update
6
+ credit_usage_service.call
7
+ redirect_to params[:_return_to]
8
+ end
9
+
10
+ def destroy
11
+ credit_usage_service.clean_credit_note_adjustments!
12
+ redirect_to params[:_return_to]
13
+ end
14
+
15
+ private
16
+
17
+ def load_cart
18
+ @cart = ProductList.find_by_token(params[:cart_id]) || current_cart
19
+ end
20
+
21
+ def credit_usage_service
22
+ @credit_usage_service ||= Stall.config.service_for(:credit_usage).new(
23
+ @cart, amount: params[:amount]
24
+ )
25
+ end
26
+ end
27
+ end
@@ -8,7 +8,7 @@ module Stall
8
8
 
9
9
  def update
10
10
  respond_to do |format|
11
- service = Stall::CartUpdateService.new(@cart, cart_params)
11
+ service = Stall.config.service_for(:cart_update).new(@cart, cart_params)
12
12
 
13
13
  if service.call
14
14
  format.html.xhr do
@@ -42,24 +42,20 @@ module Stall
42
42
 
43
43
  def load_cart
44
44
  @cart = current_cart
45
+
46
+ unless @cart.checkoutable?
47
+ if archived_paid_cart?
48
+ @cart = archived_paid_cart
49
+ else
50
+ redirect_from_uncheckoutable_cart!
51
+ end
52
+ end
45
53
  end
46
54
 
47
55
  def find_cart(identifier, ensure_active_cart = true)
48
56
  super(identifier, false)
49
57
  end
50
58
 
51
- def ensure_cart_checkoutable
52
- unless @cart.active? || @step.allow_inactive_carts?
53
- remove_cart_from_cookies(@cart.identifier)
54
- @cart = @cart.class.new
55
- end
56
-
57
- unless @cart.checkoutable?
58
- flash[:error] = t('stall.checkout.shared.not_checkoutable')
59
- redirect_to_referrer_or_root
60
- end
61
- end
62
-
63
59
  def load_step
64
60
  @wizard = @cart.wizard.new(@cart)
65
61
 
@@ -79,6 +75,20 @@ module Stall
79
75
  end
80
76
  end
81
77
 
78
+ def ensure_cart_checkoutable
79
+ unless @cart.active? || @step.allow_inactive_carts?
80
+ remove_cart_from_cookies(@cart.identifier)
81
+ @cart = @cart.class.new
82
+ end
83
+
84
+ redirect_from_uncheckoutable_cart! unless @cart.checkoutable?
85
+ end
86
+
87
+ def redirect_from_uncheckoutable_cart!
88
+ flash[:error] = t('stall.checkout.shared.not_checkoutable')
89
+ redirect_to_referrer_or_root
90
+ end
91
+
82
92
  def redirect_to_referrer_or_root
83
93
  referrer = URI.parse(request.referrer).path if request.referrer
84
94
 
@@ -6,6 +6,7 @@ module Stall
6
6
 
7
7
  def show
8
8
  @cart.reset_state!
9
+ Stall.config.service_for(:cart_update).new(@cart).refresh_associated_services!
9
10
  redirect_to step_path
10
11
  end
11
12
 
@@ -6,6 +6,7 @@ module Stall
6
6
  if service.call
7
7
  @quantity = params[:line_item][:quantity].to_i
8
8
  @line_item = service.line_item
9
+ @widget_partial = render_to_string(partial: 'stall/carts/widget', locals: { cart: cart })
9
10
  render partial: 'added'
10
11
  else
11
12
  @line_item = service.line_item
@@ -2,10 +2,25 @@ module Stall
2
2
  class PaymentsController < Stall::ApplicationController
3
3
  skip_before_action :verify_authenticity_token, only: [:notify]
4
4
 
5
+ # Avoid user-requested payment notifications to remove the cart from cookies
6
+ # as soon as it is paid
7
+ skip_after_action :store_cart_to_cookies
8
+
5
9
  def notify
6
10
  service = Stall.config.service_for(:payment_notification).new(params[:gateway], request)
7
11
  service.call
8
- render service.rendering_options
12
+
13
+ # TODO : This is not the cleanest solution but an API change here would
14
+ # imply to rewrite most of the existing gateways.
15
+ #
16
+ # This should be done as soon as we can to avoid keeping this
17
+ # dirty fix for too long
18
+ #
19
+ if (redirect_location = service.rendering_options[:redirect_location])
20
+ redirect_to redirect_location
21
+ else
22
+ render service.rendering_options
23
+ end
9
24
  end
10
25
  end
11
26
  end
@@ -0,0 +1,25 @@
1
+ module Stall
2
+ module CreditNotesHelper
3
+ def available_customer_credit_for?(cart)
4
+ cart.customer.try(:credit?, cart.currency) || credit_used_for?(cart)
5
+ end
6
+
7
+ def maximum_credit_usage_for(cart)
8
+ credit_usage_service_for(cart).amount.to_d
9
+ end
10
+
11
+ def current_customer_credit_for(cart)
12
+ credit_usage_service_for(cart).credit
13
+ end
14
+
15
+ def credit_used_for?(cart)
16
+ credit_usage_service_for(cart).credit_used?
17
+ end
18
+
19
+ private
20
+
21
+ def credit_usage_service_for(cart)
22
+ Stall.config.service_for(:credit_usage).new(cart)
23
+ end
24
+ end
25
+ end
@@ -5,7 +5,9 @@ module Stall
5
5
  end
6
6
 
7
7
  def current_stall_user
8
- send(Stall.config.default_user_helper_method)
8
+ if (method = Stall.config.default_user_helper_method) && respond_to?(method, true)
9
+ send(Stall.config.default_user_helper_method)
10
+ end
9
11
  end
10
12
 
11
13
  # Copy e-mail error messages from user to customer, allowing them to be
@@ -0,0 +1,2 @@
1
+ class BillingAddress < Address
2
+ end
@@ -0,0 +1,3 @@
1
+ class CartCreditNoteAdjustment < Adjustment
2
+ include Stall::Models::CartCreditNoteAdjustment
3
+ end