stall 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/para/stall/inputs/variant-select.coffee +4 -3
- data/app/assets/javascripts/stall.coffee +3 -1
- data/app/assets/javascripts/stall/add-product-to-wish-list-button.coffee +99 -0
- data/app/assets/javascripts/stall/add-to-wish-list-button.coffee +17 -0
- data/app/assets/javascripts/stall/product-list-form.coffee +31 -0
- data/app/controllers/stall/cart_line_items_controller.rb +21 -0
- data/app/controllers/stall/curated_product_lists_controller.rb +6 -0
- data/app/controllers/stall/line_items_controller.rb +14 -7
- data/app/controllers/stall/products_breadcrumbs.rb +8 -1
- data/app/controllers/stall/products_controller.rb +12 -0
- data/app/controllers/stall/wish_list_line_items_controller.rb +52 -0
- data/app/controllers/stall/wish_lists_controller.rb +7 -0
- data/app/helpers/stall/add_to_cart_helper.rb +3 -2
- data/app/helpers/stall/add_to_wish_list_helper.rb +30 -0
- data/app/helpers/stall/products_helper.rb +13 -0
- data/app/models/stall/models/cart.rb +0 -6
- data/app/models/stall/models/customer.rb +9 -0
- data/app/models/stall/models/line_item.rb +13 -0
- data/app/models/stall/models/product.rb +1 -1
- data/app/models/stall/models/product_list.rb +4 -0
- data/app/models/stall/models/variant.rb +4 -0
- data/app/models/stall/models/wish_list.rb +18 -0
- data/app/models/wish_list.rb +3 -0
- data/app/services/stall/add_to_cart_service.rb +4 -47
- data/app/services/stall/add_to_product_list_service.rb +63 -0
- data/app/services/stall/add_to_wish_list_service.rb +17 -0
- data/app/services/stall/available_stocks_service.rb +1 -1
- data/app/services/stall/shipping_fee_calculator_service.rb +22 -9
- data/app/views/admin/products/_form.html.haml +5 -0
- data/app/views/checkout/steps/_informations.html.haml +1 -1
- data/app/views/stall/addresses/_fields.html.haml +4 -4
- data/app/views/stall/{line_items → cart_line_items}/_add_error.html.haml +0 -0
- data/app/views/stall/{line_items → cart_line_items}/_added.html.haml +2 -2
- data/app/views/stall/{line_items → cart_line_items}/_form.html.haml +1 -1
- data/app/views/stall/carts/_widget.html.haml +1 -1
- data/app/views/stall/carts/show.html.haml +4 -4
- data/app/views/stall/curated_product_lists/show.html.haml +1 -1
- data/app/views/stall/customers/_fields.html.haml +1 -1
- data/app/views/stall/customers/_sign_in.html.haml +2 -2
- data/app/views/stall/products/_list.html.haml +2 -0
- data/app/views/stall/products/_product.html.haml +2 -1
- data/app/views/stall/wish_list_line_items/_add_error.html.haml +17 -0
- data/app/views/stall/wish_list_line_items/_added.html.haml +19 -0
- data/app/views/stall/wish_list_line_items/_button.html.haml +3 -0
- data/app/views/stall/wish_list_line_items/_form.html.haml +12 -0
- data/app/views/stall/wish_lists/show.html.haml +21 -0
- data/config/locales/stall.fr.yml +26 -0
- data/db/migrate/20170425085606_add_weight_to_stall_products_and_variants.rb +6 -0
- data/db/migrate/20170426163450_add_vat_rate_to_stall_products.rb +5 -0
- data/db/migrate/20170522062334_change_variants_weight_default_to_nil.rb +11 -0
- data/lib/generators/stall/install/templates/initializer.rb +18 -0
- data/lib/stall.rb +1 -0
- data/lib/stall/addressable.rb +35 -4
- data/lib/stall/addresses/copy_source_to_target.rb +14 -10
- data/lib/stall/addresses/prefill_target_from_source.rb +10 -6
- data/lib/stall/config.rb +4 -0
- data/lib/stall/engine.rb +1 -0
- data/lib/stall/routes.rb +42 -19
- data/lib/stall/sellable.rb +1 -1
- data/lib/stall/shipping/calculator.rb +1 -1
- data/lib/stall/shipping/country_weight_table_calculator.rb +3 -2
- data/lib/stall/version.rb +1 -1
- data/lib/stall/wish_list_helper.rb +93 -0
- metadata +26 -6
- data/app/assets/javascripts/stall/cart-form.coffee +0 -28
@@ -29,6 +29,7 @@ module Stall
|
|
29
29
|
|
30
30
|
before_validation :restore_valid_quantity
|
31
31
|
before_validation :refresh_total_prices
|
32
|
+
before_validation :refresh_weight
|
32
33
|
|
33
34
|
scope :ordered, -> { order(created_at: :asc) }
|
34
35
|
end
|
@@ -43,6 +44,14 @@ module Stall
|
|
43
44
|
product_list.try(:currency) || Money.default_currency
|
44
45
|
end
|
45
46
|
|
47
|
+
def weight
|
48
|
+
read_store_attribute(:data, :weight).presence || Stall.config.default_product_weight
|
49
|
+
end
|
50
|
+
|
51
|
+
def total_weight
|
52
|
+
weight * quantity
|
53
|
+
end
|
54
|
+
|
46
55
|
private
|
47
56
|
|
48
57
|
# TODO : Stocks availibility handling
|
@@ -61,6 +70,10 @@ module Stall
|
|
61
70
|
restore_quantity!
|
62
71
|
end
|
63
72
|
end
|
73
|
+
|
74
|
+
def refresh_weight
|
75
|
+
self.weight = sellable.weight if sellable.respond_to?(:weight)
|
76
|
+
end
|
64
77
|
end
|
65
78
|
end
|
66
79
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Stall
|
2
|
+
module Models
|
3
|
+
module WishList
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def includes_product?(product)
|
7
|
+
line_item_for_product(product).present?
|
8
|
+
end
|
9
|
+
|
10
|
+
def line_item_for_product(product)
|
11
|
+
line_items.find do |line_item|
|
12
|
+
line_item.sellable_type == 'Variant' &&
|
13
|
+
product.variant_ids.include?(line_item.sellable_id)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,18 +1,11 @@
|
|
1
1
|
module Stall
|
2
|
-
class AddToCartService < Stall::
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(cart, params)
|
6
|
-
@cart = cart
|
7
|
-
@params = params
|
8
|
-
end
|
2
|
+
class AddToCartService < Stall::AddToProductListService
|
3
|
+
alias_method :cart, :product_list
|
9
4
|
|
10
5
|
def call
|
11
6
|
return false unless line_item_valid?
|
12
7
|
|
13
|
-
unless assemble_identical_line_items
|
14
|
-
cart.line_items << line_item
|
15
|
-
end
|
8
|
+
cart.line_items << line_item unless assemble_identical_line_items
|
16
9
|
|
17
10
|
cart.save.tap do |saved|
|
18
11
|
return false unless saved
|
@@ -32,46 +25,10 @@ module Stall
|
|
32
25
|
stock_service.on_hand?(line_item)
|
33
26
|
end
|
34
27
|
|
35
|
-
def line_item
|
36
|
-
@line_item ||= sellable.to_line_item.tap do |line_item|
|
37
|
-
line_item.quantity = line_item_params[:quantity]
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
28
|
private
|
42
29
|
|
43
|
-
def assemble_identical_line_items
|
44
|
-
# Find an existing line item which is like our new one
|
45
|
-
existing_line_item = cart.line_items.find do |li|
|
46
|
-
line_item.like?(li)
|
47
|
-
end
|
48
|
-
|
49
|
-
# If we found one, we assemble both line items into the old one, to avoid
|
50
|
-
# duplicating the same sellables in the product list
|
51
|
-
if existing_line_item
|
52
|
-
existing_line_item.quantity += line_item.quantity
|
53
|
-
@line_item = existing_line_item
|
54
|
-
else
|
55
|
-
false
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def sellable
|
60
|
-
@sellable ||= sellable_class.find(line_item_params[:sellable_id])
|
61
|
-
end
|
62
|
-
|
63
|
-
def sellable_class
|
64
|
-
@sellable_class ||= line_item_params[:sellable_type].camelize.constantize
|
65
|
-
end
|
66
|
-
|
67
30
|
def shipping_fee_service
|
68
|
-
@shipping_fee_service ||= Stall
|
69
|
-
end
|
70
|
-
|
71
|
-
def line_item_params
|
72
|
-
@line_item_params ||= params.require(:line_item).permit(
|
73
|
-
:sellable_type, :sellable_id, :quantity
|
74
|
-
)
|
31
|
+
@shipping_fee_service ||= Stall.config.service_for(:shipping_fee_calculator).new(cart)
|
75
32
|
end
|
76
33
|
end
|
77
34
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Stall
|
2
|
+
class AddToProductListService < Stall::BaseService
|
3
|
+
attr_reader :product_list, :params
|
4
|
+
|
5
|
+
def initialize(product_list, params)
|
6
|
+
@product_list = product_list
|
7
|
+
@params = params
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
fail NotImplementedError,
|
12
|
+
'Override #call in Stall::AddToProductListService subclasses'
|
13
|
+
end
|
14
|
+
|
15
|
+
def line_item
|
16
|
+
@line_item ||= sellable.to_line_item.tap do |line_item|
|
17
|
+
line_item.quantity = quantity
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def line_item_params?
|
22
|
+
line_item_params.any?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def assemble_identical_line_items
|
28
|
+
# Find an existing line item which is like our new one
|
29
|
+
existing_line_item = product_list.line_items.find do |li|
|
30
|
+
line_item.like?(li)
|
31
|
+
end
|
32
|
+
|
33
|
+
# If we found one, we assemble both line items into the old one, to avoid
|
34
|
+
# duplicating the same sellables in the product list
|
35
|
+
if existing_line_item
|
36
|
+
existing_line_item.quantity += line_item.quantity
|
37
|
+
@line_item = existing_line_item
|
38
|
+
else
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def sellable
|
44
|
+
@sellable ||= sellable_class.find(line_item_params[:sellable_id])
|
45
|
+
end
|
46
|
+
|
47
|
+
def sellable_class
|
48
|
+
@sellable_class ||= line_item_params[:sellable_type].camelize.constantize
|
49
|
+
end
|
50
|
+
|
51
|
+
def quantity
|
52
|
+
@quantity ||= line_item_params[:quantity]
|
53
|
+
end
|
54
|
+
|
55
|
+
def line_item_params
|
56
|
+
@line_item_params ||= params.require(:line_item).permit(
|
57
|
+
:sellable_type, :sellable_id, :quantity
|
58
|
+
)
|
59
|
+
rescue ActionController::ParameterMissing
|
60
|
+
{}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Stall
|
2
|
+
class AddToWishListService < Stall::AddToProductListService
|
3
|
+
alias_method :wish_list, :product_list
|
4
|
+
|
5
|
+
def call
|
6
|
+
return false unless line_item.valid?
|
7
|
+
wish_list.line_items << line_item unless assemble_identical_line_items
|
8
|
+
wish_list.save
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(variant)
|
12
|
+
@sellable = variant
|
13
|
+
@quantity = 1
|
14
|
+
call
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -9,24 +9,37 @@ module Stall
|
|
9
9
|
def call
|
10
10
|
return unless cart.shipment && cart.shipment.shipping_method
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
update_price_for(cart.shipment, calculator) if calculator
|
13
|
+
end
|
14
|
+
|
15
|
+
def available?
|
16
|
+
cart.line_items.length > 0 &&
|
17
|
+
cart.try(:shipping_address) &&
|
18
|
+
cart.try(:shipment).try(:shipping_method)
|
19
|
+
end
|
14
20
|
|
15
|
-
|
16
|
-
calculator = calculator_class.new(cart, cart.shipment.shipping_method)
|
21
|
+
private
|
17
22
|
|
18
|
-
|
23
|
+
def update_price_for(shipment, calculator)
|
24
|
+
if calculator.price
|
25
|
+
shipment.update_attributes(
|
19
26
|
price: calculator.price,
|
20
27
|
eot_price: calculator.eot_price,
|
21
28
|
vat_rate: calculator.vat_rate
|
22
29
|
)
|
30
|
+
else
|
31
|
+
shipment.errors.add(:price, :unavailable)
|
23
32
|
end
|
24
33
|
end
|
25
34
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
35
|
+
def calculator
|
36
|
+
@calculator ||= calculator_class && calculator_class.new(cart, cart.shipment.shipping_method)
|
37
|
+
end
|
38
|
+
|
39
|
+
def calculator_class
|
40
|
+
@calculator_class ||= Stall::Shipping::Calculator.for(
|
41
|
+
cart.shipment.shipping_method.identifier
|
42
|
+
) if cart.shipment.try(:shipping_method)
|
30
43
|
end
|
31
44
|
end
|
32
45
|
end
|
@@ -8,6 +8,8 @@
|
|
8
8
|
= form.input :product_category, as: :selectize, collection: Para.components.product_categories.resources.leaves
|
9
9
|
= form.input :manufacturer, as: :selectize
|
10
10
|
|
11
|
+
= form.input :vat_rate, hint: t('para.stall.products.vat_rate_hint', rate: number_to_percentage(Stall.config.vat_rate), precision: 2).html_safe
|
12
|
+
|
11
13
|
= form.input :suggested_products, as: :selectize
|
12
14
|
|
13
15
|
= tabs.tab :images, icon: 'picture-o' do
|
@@ -20,4 +22,7 @@
|
|
20
22
|
= tabs.tab :variants, icon: 'list' do
|
21
23
|
= form.input :variants, as: :variants_matrix
|
22
24
|
|
25
|
+
= tabs.tab :shipping, icon: 'truck' do
|
26
|
+
= form.input :weight, hint: t('para.stall.products.weight_hint').html_safe
|
27
|
+
|
23
28
|
= form.actions
|
@@ -25,7 +25,7 @@
|
|
25
25
|
= form.input :terms, label: false do
|
26
26
|
= form.check_box :terms, id: 'terms-checkbox'
|
27
27
|
= form.label :terms, t('stall.checkout.informations.accept_the')
|
28
|
-
= link_to Stall.config.terms_path, target: '_blank' do
|
28
|
+
= link_to Stall.config.terms_path, target: '_blank', rel: 'nofollow' do
|
29
29
|
= form.object.class.human_attribute_name(:terms)
|
30
30
|
|
31
31
|
%button.btn.btn-primary.btn-lg.btn-block{ type: 'submit' }
|
@@ -1,10 +1,10 @@
|
|
1
1
|
.addresses-fields{ data: { :'addresses-fields' => true } }
|
2
2
|
%fieldset{ data: { :'address-form' => :shipping } }
|
3
|
-
= form.fields_for :shipping_address do |address_form|
|
3
|
+
= form.fields_for :shipping_address, form.object.shipping_address(force_actual_address: true) do |address_form|
|
4
4
|
= render partial: 'stall/addresses/nested_fields', locals: { form: address_form }
|
5
5
|
|
6
|
-
= form.input :use_another_address_for_billing, as: :boolean, input_html: { checked: (params[:use_another_address_for_billing] || form.object.billing_address?), data: { :'use-another-address-for-billing' => true } }
|
6
|
+
= form.input :use_another_address_for_billing, as: :boolean, input_html: { checked: (params[:use_another_address_for_billing] == '1' || form.object.billing_address?), data: { :'use-another-address-for-billing' => true } }
|
7
7
|
|
8
|
-
%fieldset{ class: ('hidden' unless params[:use_another_address_for_billing] == '1'), data: { :'address-form' => :billing } }
|
9
|
-
= form.fields_for :billing_address do |address_form|
|
8
|
+
%fieldset{ class: ('hidden' unless params[:use_another_address_for_billing] == '1' || form.object.billing_address?), data: { :'address-form' => :billing } }
|
9
|
+
= form.fields_for :billing_address, form.object.billing_address(force_actual_address: true) do |address_form|
|
10
10
|
= render partial: 'stall/addresses/nested_fields', locals: { form: address_form }
|
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
.modal.fade{ role: 'dialog', tabindex: '-1', data: { :'cart' => { token:
|
1
|
+
.modal.fade{ role: 'dialog', tabindex: '-1', data: { :'cart' => { token: @product_list.token, :'total-quantity' => @product_list.total_quantity, :'widget-markup' => @widget_partial } } }
|
2
2
|
.modal-dialog
|
3
3
|
.modal-content
|
4
4
|
.modal-header
|
@@ -19,5 +19,5 @@
|
|
19
19
|
%button.btn.btn-default{ type: 'button', data: { dismiss: 'modal' } }
|
20
20
|
= t('stall.line_items.added.continue_shopping')
|
21
21
|
|
22
|
-
= link_to cart_path(@
|
22
|
+
= link_to cart_path(@product_list), class: 'btn btn-primary', rel: 'nofollow' do
|
23
23
|
= t('stall.carts.actions.view_cart')
|
@@ -1,6 +1,6 @@
|
|
1
1
|
= simple_form_for line_item, as: :line_item, url: cart_line_items_path(cart), remote: true, data: { :'add-to-cart-form' => true, :'error-messages' => { choose: t('stall.line_items.errors.choose'), quantity: t('stall.line_items.errors.quantity') } } do |form|
|
2
2
|
= form.hidden_field :sellable_type, value: 'Variant'
|
3
|
-
= form.input_field :sellable, as: :variant_select, product:
|
3
|
+
= form.input_field :sellable, as: :variant_select, product: product
|
4
4
|
|
5
5
|
.input-group
|
6
6
|
= form.input_field :quantity, spinner: false, value: 1, class: 'form-group', data: { :'quantity-field' => true }
|
@@ -1,5 +1,5 @@
|
|
1
|
-
.cart-recap{ data: { :'
|
2
|
-
= simple_form_for @cart, url: cart_path(@cart), as: :cart, remote: true, data: { :'
|
1
|
+
.cart-recap{ data: { :'product-list-recap' => true } }
|
2
|
+
= simple_form_for @cart, url: cart_path(@cart), as: :cart, remote: true, data: { :'product-list-form' => true } do |form|
|
3
3
|
%table.table.table-striped
|
4
4
|
%thead
|
5
5
|
%tr
|
@@ -56,10 +56,10 @@
|
|
56
56
|
%td
|
57
57
|
|
58
58
|
.form-actions
|
59
|
-
%button.btn.btn-default{ type: 'submit', data: { :'
|
59
|
+
%button.btn.btn-default{ type: 'submit', data: { :'product-list-update-button' => true } }
|
60
60
|
= t('stall.carts.recap.update')
|
61
61
|
|
62
|
-
= link_to checkout_path(current_cart.identifier), class: 'btn btn-primary' do
|
62
|
+
= link_to checkout_path(current_cart.identifier), class: 'btn btn-primary', rel: 'nofollow' do
|
63
63
|
= t('stall.carts.recap.validate')
|
64
64
|
|
65
65
|
|
@@ -5,4 +5,4 @@
|
|
5
5
|
= render partial: 'stall/products/filters', locals: { search: @search, products: @filterable_products }
|
6
6
|
|
7
7
|
.col-md-8
|
8
|
-
= render partial: 'stall/products/list', locals: { products: @products }
|
8
|
+
= render partial: 'stall/products/list', locals: { products: @products, parent: @curated_product_list }
|
@@ -26,7 +26,7 @@
|
|
26
26
|
%legend.omniauth-sign-up= t('stall.checkout.sign_in.omniauth_sign_up')
|
27
27
|
|
28
28
|
- Stall.config.omniauth_providers.each do |provider|
|
29
|
-
= link_to user_omniauth_redirect_path(provider.name, _redirect_to: request.path), class: 'btn btn-default' do
|
29
|
+
= link_to user_omniauth_redirect_path(provider.name, _redirect_to: request.path), class: 'btn btn-default', rel: 'nofollow' do
|
30
30
|
= fa_icon provider.icon
|
31
31
|
= t('stall.omniauth.sign_in_with', provider: provider.display_name)
|
32
32
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
.panel-heading
|
3
3
|
%i.fa.fa-user
|
4
4
|
= t('stall.checkout.sign_in.already_have_an_account')
|
5
|
-
= link_to '#sign-in-form', data: { toggle: 'collapse' } do
|
5
|
+
= link_to '#sign-in-form', data: { toggle: 'collapse' }, rel: 'nofollow' do
|
6
6
|
= t('stall.checkout.sign_in.click_to_sign_in')
|
7
7
|
|
8
8
|
#sign-in-form.panel-body.collapse{ data: { :'sign-in-form' => true } }
|
@@ -27,6 +27,6 @@
|
|
27
27
|
%legend.omniauth-sign-in= t('stall.checkout.sign_in.omniauth_sign_in')
|
28
28
|
|
29
29
|
- Stall.config.omniauth_providers.each do |provider|
|
30
|
-
= link_to user_omniauth_redirect_path(provider.name, _redirect_to: request.path), class: 'btn btn-default' do
|
30
|
+
= link_to user_omniauth_redirect_path(provider.name, _redirect_to: request.path), class: 'btn btn-default', rel: 'nofollow' do
|
31
31
|
= fa_icon provider.icon
|
32
32
|
= t('stall.omniauth.sign_in_with', provider: provider.display_name)
|
@@ -1,4 +1,5 @@
|
|
1
|
-
= link_to product_path(product), class: 'product' do
|
1
|
+
= link_to product_path(product), class: 'product', data: { :'product-id' => product.id } do
|
2
2
|
%span.product-name= product.name
|
3
3
|
= image_tag product.image.url(:thumb), class: 'img-responsive'
|
4
4
|
= number_to_currency(product.price)
|
5
|
+
= add_to_wish_list_button(product)
|