stall 0.2.0 → 0.3.1

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.
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