solidus_frontend_devise_token_auth 2.8.0.alpha.2
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 +7 -0
- data/LICENSE +26 -0
- data/README.md +42 -0
- data/Rakefile +17 -0
- data/app/assets/config/solidus_frontend_manifest.js +4 -0
- data/app/assets/images/credit_cards/amex_cid.gif +0 -0
- data/app/assets/images/credit_cards/credit_card.gif +0 -0
- data/app/assets/images/credit_cards/discover_cid.gif +0 -0
- data/app/assets/images/credit_cards/icons/american_express.png +0 -0
- data/app/assets/images/credit_cards/icons/cirrus.png +0 -0
- data/app/assets/images/credit_cards/icons/delta.png +0 -0
- data/app/assets/images/credit_cards/icons/diners_club.png +0 -0
- data/app/assets/images/credit_cards/icons/directdebit.png +0 -0
- data/app/assets/images/credit_cards/icons/discover.png +0 -0
- data/app/assets/images/credit_cards/icons/egold.png +0 -0
- data/app/assets/images/credit_cards/icons/maestro.png +0 -0
- data/app/assets/images/credit_cards/icons/master.png +0 -0
- data/app/assets/images/credit_cards/icons/paypal.png +0 -0
- data/app/assets/images/credit_cards/icons/solo.png +0 -0
- data/app/assets/images/credit_cards/icons/switch.png +0 -0
- data/app/assets/images/credit_cards/icons/visa.png +0 -0
- data/app/assets/images/credit_cards/icons/visaelectron.png +0 -0
- data/app/assets/images/credit_cards/icons/westernunion.png +0 -0
- data/app/assets/images/credit_cards/icons/wirecard.png +0 -0
- data/app/assets/images/credit_cards/icons/worldpay.png +0 -0
- data/app/assets/images/credit_cards/master_cid.jpg +0 -0
- data/app/assets/images/credit_cards/visa_cid.gif +0 -0
- data/app/assets/images/favicon.ico +0 -0
- data/app/assets/images/icons/add-to-cart.png +0 -0
- data/app/assets/images/icons/checkout.png +0 -0
- data/app/assets/images/icons/delete.png +0 -0
- data/app/assets/images/icons/update.png +0 -0
- data/app/assets/images/spinner.gif +0 -0
- data/app/assets/images/spree/frontend/cart.png +0 -0
- data/app/assets/javascripts/spree/frontend/cart.js +30 -0
- data/app/assets/javascripts/spree/frontend/checkout/address.js +127 -0
- data/app/assets/javascripts/spree/frontend/checkout/coupon-code.js +41 -0
- data/app/assets/javascripts/spree/frontend/checkout/payment.js +55 -0
- data/app/assets/javascripts/spree/frontend/checkout.js +24 -0
- data/app/assets/javascripts/spree/frontend/locale_selector.js +5 -0
- data/app/assets/javascripts/spree/frontend/product.js +81 -0
- data/app/assets/javascripts/spree/frontend.js +5 -0
- data/app/assets/stylesheets/spree/frontend/_skeleton.scss +242 -0
- data/app/assets/stylesheets/spree/frontend/_variables.scss +65 -0
- data/app/assets/stylesheets/spree/frontend/screen.css.scss +1387 -0
- data/app/assets/stylesheets/spree/frontend.css +5 -0
- data/app/controllers/spree/checkout_controller.rb +236 -0
- data/app/controllers/spree/content_controller.rb +11 -0
- data/app/controllers/spree/home_controller.rb +14 -0
- data/app/controllers/spree/locale_controller.rb +20 -0
- data/app/controllers/spree/orders_controller.rb +138 -0
- data/app/controllers/spree/products_controller.rb +52 -0
- data/app/controllers/spree/store_controller.rb +30 -0
- data/app/controllers/spree/taxons_controller.rb +31 -0
- data/app/helpers/spree/orders_helper.rb +18 -0
- data/app/helpers/spree/taxon_filters_helper.rb +13 -0
- data/app/models/spree/frontend_configuration.rb +10 -0
- data/app/views/spree/address/_form.html.erb +96 -0
- data/app/views/spree/address/_form_hidden.html.erb +12 -0
- data/app/views/spree/checkout/_address.html.erb +33 -0
- data/app/views/spree/checkout/_confirm.html.erb +25 -0
- data/app/views/spree/checkout/_coupon_code.html.erb +12 -0
- data/app/views/spree/checkout/_delivery.html.erb +108 -0
- data/app/views/spree/checkout/_payment.html.erb +67 -0
- data/app/views/spree/checkout/_summary.html.erb +76 -0
- data/app/views/spree/checkout/_terms_and_conditions.en.html.erb +1 -0
- data/app/views/spree/checkout/edit.html.erb +32 -0
- data/app/views/spree/checkout/existing_payment/_gateway.html.erb +9 -0
- data/app/views/spree/checkout/payment/_check.html.erb +0 -0
- data/app/views/spree/checkout/payment/_gateway.html.erb +36 -0
- data/app/views/spree/content/cvv.html.erb +13 -0
- data/app/views/spree/home/index.html.erb +12 -0
- data/app/views/spree/layouts/spree_application.html.erb +36 -0
- data/app/views/spree/orders/_adjustment_row.html.erb +8 -0
- data/app/views/spree/orders/_adjustments.html.erb +24 -0
- data/app/views/spree/orders/_form.html.erb +29 -0
- data/app/views/spree/orders/_line_item.html.erb +34 -0
- data/app/views/spree/orders/edit.html.erb +47 -0
- data/app/views/spree/orders/show.html.erb +22 -0
- data/app/views/spree/payments/_payment.html.erb +18 -0
- data/app/views/spree/products/_cart_form.html.erb +65 -0
- data/app/views/spree/products/_image.html.erb +8 -0
- data/app/views/spree/products/_promotions.html.erb +19 -0
- data/app/views/spree/products/_properties.html.erb +15 -0
- data/app/views/spree/products/_taxons.html.erb +14 -0
- data/app/views/spree/products/_thumbnails.html.erb +21 -0
- data/app/views/spree/products/index.html.erb +27 -0
- data/app/views/spree/products/show.html.erb +51 -0
- data/app/views/spree/shared/_address.html.erb +38 -0
- data/app/views/spree/shared/_filters.html.erb +27 -0
- data/app/views/spree/shared/_footer.html.erb +6 -0
- data/app/views/spree/shared/_head.html.erb +14 -0
- data/app/views/spree/shared/_header.html.erb +5 -0
- data/app/views/spree/shared/_image.html.erb +12 -0
- data/app/views/spree/shared/_link_to_cart.html.erb +1 -0
- data/app/views/spree/shared/_locale_selector.html.erb +25 -0
- data/app/views/spree/shared/_login_bar_items.html.erb +1 -0
- data/app/views/spree/shared/_main_nav_bar.html.erb +12 -0
- data/app/views/spree/shared/_nav_bar.html.erb +9 -0
- data/app/views/spree/shared/_order_details.html.erb +146 -0
- data/app/views/spree/shared/_products.html.erb +51 -0
- data/app/views/spree/shared/_search.html.erb +11 -0
- data/app/views/spree/shared/_shipment_tracking.html.erb +9 -0
- data/app/views/spree/shared/_sidebar.html.erb +3 -0
- data/app/views/spree/shared/_taxonomies.html.erb +9 -0
- data/app/views/spree/shared/unauthorized.html.erb +0 -0
- data/app/views/spree/store/cart_link.html.erb +1 -0
- data/app/views/spree/taxons/_taxon.html.erb +4 -0
- data/app/views/spree/taxons/show.html.erb +20 -0
- data/config/initializers/assets.rb +3 -0
- data/config/initializers/canonical_rails.rb +16 -0
- data/config/routes.rb +33 -0
- data/lib/generators/solidus/views/override_generator.rb +49 -0
- data/lib/solidus_frontend.rb +3 -0
- data/lib/spree/frontend/engine.rb +13 -0
- data/lib/spree/frontend/middleware/seo_assist.rb +52 -0
- data/lib/spree/frontend.rb +15 -0
- data/lib/spree_frontend.rb +3 -0
- data/lib/tasks/rake_util.rb +18 -0
- data/lib/tasks/taxon.rake +16 -0
- data/script/rails +10 -0
- data/solidus_frontend.gemspec +35 -0
- data/spec/controllers/controller_helpers_spec.rb +30 -0
- data/spec/controllers/locale_controller_spec.rb +57 -0
- data/spec/controllers/spree/checkout_controller_spec.rb +539 -0
- data/spec/controllers/spree/checkout_controller_with_views_spec.rb +37 -0
- data/spec/controllers/spree/content_controller_spec.rb +9 -0
- data/spec/controllers/spree/current_order_tracking_spec.rb +47 -0
- data/spec/controllers/spree/home_controller_spec.rb +29 -0
- data/spec/controllers/spree/orders_controller_ability_spec.rb +93 -0
- data/spec/controllers/spree/orders_controller_spec.rb +225 -0
- data/spec/controllers/spree/orders_controller_transitions_spec.rb +33 -0
- data/spec/controllers/spree/products_controller_spec.rb +38 -0
- data/spec/controllers/spree/taxons_controller_spec.rb +14 -0
- data/spec/features/address_spec.rb +78 -0
- data/spec/features/automatic_promotion_adjustments_spec.rb +49 -0
- data/spec/features/caching/products_spec.rb +48 -0
- data/spec/features/caching/taxons_spec.rb +21 -0
- data/spec/features/cart_spec.rb +85 -0
- data/spec/features/checkout_spec.rb +690 -0
- data/spec/features/checkout_unshippable_spec.rb +37 -0
- data/spec/features/coupon_code_spec.rb +266 -0
- data/spec/features/currency_spec.rb +20 -0
- data/spec/features/free_shipping_promotions_spec.rb +60 -0
- data/spec/features/locale_spec.rb +27 -0
- data/spec/features/order_spec.rb +73 -0
- data/spec/features/products_spec.rb +291 -0
- data/spec/features/promotion_code_invalidation_spec.rb +54 -0
- data/spec/features/quantity_promotions_spec.rb +130 -0
- data/spec/features/taxons_spec.rb +158 -0
- data/spec/features/template_rendering_spec.rb +20 -0
- data/spec/fixtures/thinking-cat.jpg +0 -0
- data/spec/generators/solidus/views/override_generator_spec.rb +50 -0
- data/spec/helpers/base_helper_spec.rb +13 -0
- data/spec/helpers/order_helper_spec.rb +14 -0
- data/spec/helpers/taxon_filters_helper_spec.rb +12 -0
- data/spec/spec_helper.rb +106 -0
- data/spec/support/features/fill_in_with_force.rb +12 -0
- data/spec/support/shared_contexts/checkout_setup.rb +12 -0
- data/spec/support/shared_contexts/custom_products.rb +28 -0
- data/spec/support/shared_contexts/locales.rb +16 -0
- data/spec/views/spree/checkout/_summary_spec.rb +11 -0
- metadata +337 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
# This is somewhat contrary to standard REST convention since there is not
|
|
5
|
+
# actually a Checkout object. There's enough distinct logic specific to
|
|
6
|
+
# checkout which has nothing to do with updating an order that this approach
|
|
7
|
+
# is warranted.
|
|
8
|
+
class CheckoutController < Spree::StoreController
|
|
9
|
+
before_action :load_order
|
|
10
|
+
around_action :lock_order
|
|
11
|
+
before_action :set_state_if_present
|
|
12
|
+
|
|
13
|
+
before_action :ensure_order_not_completed
|
|
14
|
+
before_action :ensure_checkout_allowed
|
|
15
|
+
before_action :ensure_sufficient_stock_lines
|
|
16
|
+
before_action :ensure_valid_state
|
|
17
|
+
|
|
18
|
+
before_action :associate_user
|
|
19
|
+
before_action :check_authorization
|
|
20
|
+
before_action :apply_coupon_code
|
|
21
|
+
|
|
22
|
+
before_action :setup_for_current_state, only: [:edit, :update]
|
|
23
|
+
|
|
24
|
+
helper 'spree/orders'
|
|
25
|
+
|
|
26
|
+
rescue_from Spree::Core::GatewayError, with: :rescue_from_spree_gateway_error
|
|
27
|
+
|
|
28
|
+
# Updates the order and advances to the next state (when possible.)
|
|
29
|
+
def update
|
|
30
|
+
if update_order
|
|
31
|
+
|
|
32
|
+
assign_temp_address
|
|
33
|
+
|
|
34
|
+
unless transition_forward
|
|
35
|
+
redirect_on_failure
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if @order.completed?
|
|
40
|
+
finalize_order
|
|
41
|
+
else
|
|
42
|
+
send_to_next_state
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
else
|
|
46
|
+
render :edit
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def update_order
|
|
53
|
+
OrderUpdateAttributes.new(@order, update_params, request_env: request.headers.env).apply
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def assign_temp_address
|
|
57
|
+
@order.temporary_address = !params[:save_user_address]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def redirect_on_failure
|
|
61
|
+
flash[:error] = @order.errors.full_messages.join("\n")
|
|
62
|
+
redirect_to(checkout_state_path(@order.state))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def transition_forward
|
|
66
|
+
if @order.can_complete?
|
|
67
|
+
@order.complete
|
|
68
|
+
else
|
|
69
|
+
@order.next
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def finalize_order
|
|
74
|
+
@current_order = nil
|
|
75
|
+
set_successful_flash_notice
|
|
76
|
+
redirect_to completion_route
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def set_successful_flash_notice
|
|
80
|
+
flash.notice = t('spree.order_processed_successfully')
|
|
81
|
+
flash['order_completed'] = true
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def send_to_next_state
|
|
85
|
+
redirect_to checkout_state_path(@order.state)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def update_params
|
|
89
|
+
if update_params = massaged_params[:order]
|
|
90
|
+
update_params.permit(permitted_checkout_attributes)
|
|
91
|
+
else
|
|
92
|
+
# We currently allow update requests without any parameters in them.
|
|
93
|
+
{}
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def massaged_params
|
|
98
|
+
massaged_params = params.deep_dup
|
|
99
|
+
|
|
100
|
+
move_payment_source_into_payments_attributes(massaged_params)
|
|
101
|
+
if massaged_params[:order] && massaged_params[:order][:existing_card].present?
|
|
102
|
+
Spree::Deprecation.warn("Passing order[:existing_card] is deprecated. Send order[:wallet_payment_source_id] instead.", caller)
|
|
103
|
+
move_existing_card_into_payments_attributes(massaged_params) # deprecated
|
|
104
|
+
end
|
|
105
|
+
move_wallet_payment_source_id_into_payments_attributes(massaged_params)
|
|
106
|
+
set_payment_parameters_amount(massaged_params, @order)
|
|
107
|
+
|
|
108
|
+
massaged_params
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def ensure_valid_state
|
|
112
|
+
unless skip_state_validation?
|
|
113
|
+
if (params[:state] && !@order.has_checkout_step?(params[:state])) ||
|
|
114
|
+
(!params[:state] && !@order.has_checkout_step?(@order.state))
|
|
115
|
+
@order.state = 'cart'
|
|
116
|
+
redirect_to checkout_state_path(@order.checkout_steps.first)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Fix for https://github.com/spree/spree/issues/4117
|
|
121
|
+
# If confirmation of payment fails, redirect back to payment screen
|
|
122
|
+
if params[:state] == "confirm" && @order.payment_required? && @order.payments.valid.empty?
|
|
123
|
+
flash.keep
|
|
124
|
+
redirect_to checkout_state_path("payment")
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Should be overriden if you have areas of your checkout that don't match
|
|
129
|
+
# up to a step within checkout_steps, such as a registration step
|
|
130
|
+
def skip_state_validation?
|
|
131
|
+
false
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def load_order
|
|
135
|
+
@order = current_order
|
|
136
|
+
redirect_to(spree.cart_path) && return unless @order
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def set_state_if_present
|
|
140
|
+
if params[:state]
|
|
141
|
+
redirect_to checkout_state_path(@order.state) if @order.can_go_to_state?(params[:state]) && !skip_state_validation?
|
|
142
|
+
@order.state = params[:state]
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def ensure_checkout_allowed
|
|
147
|
+
unless @order.checkout_allowed?
|
|
148
|
+
redirect_to spree.cart_path
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def ensure_order_not_completed
|
|
153
|
+
redirect_to spree.cart_path if @order.completed?
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def ensure_sufficient_stock_lines
|
|
157
|
+
if @order.insufficient_stock_lines.present?
|
|
158
|
+
out_of_stock_items = @order.insufficient_stock_lines.collect(&:name).to_sentence
|
|
159
|
+
flash[:error] = t('spree.inventory_error_flash_for_insufficient_quantity', names: out_of_stock_items)
|
|
160
|
+
redirect_to spree.cart_path
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Provides a route to redirect after order completion
|
|
165
|
+
def completion_route
|
|
166
|
+
spree.order_path(@order)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def apply_coupon_code
|
|
170
|
+
if update_params[:coupon_code].present?
|
|
171
|
+
@order.coupon_code = update_params[:coupon_code]
|
|
172
|
+
|
|
173
|
+
handler = PromotionHandler::Coupon.new(@order).apply
|
|
174
|
+
|
|
175
|
+
if handler.error.present?
|
|
176
|
+
flash.now[:error] = handler.error
|
|
177
|
+
elsif handler.success
|
|
178
|
+
flash[:success] = handler.success
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
setup_for_current_state
|
|
182
|
+
respond_with(@order) { |format| format.html { render :edit } } && return
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def setup_for_current_state
|
|
187
|
+
method_name = :"before_#{@order.state}"
|
|
188
|
+
send(method_name) if respond_to?(method_name, true)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def before_address
|
|
192
|
+
@order.assign_default_user_addresses
|
|
193
|
+
# If the user has a default address, the previous method call takes care
|
|
194
|
+
# of setting that; but if he doesn't, we need to build an empty one here
|
|
195
|
+
default = { country_id: Spree::Country.default.id }
|
|
196
|
+
@order.build_bill_address(default) unless @order.bill_address
|
|
197
|
+
@order.build_ship_address(default) if @order.checkout_steps.include?('delivery') && !@order.ship_address
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def before_delivery
|
|
201
|
+
return if params[:order].present?
|
|
202
|
+
|
|
203
|
+
packages = @order.shipments.map(&:to_package)
|
|
204
|
+
@differentiator = Spree::Stock::Differentiator.new(@order, packages)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def before_payment
|
|
208
|
+
if @order.checkout_steps.include? "delivery"
|
|
209
|
+
packages = @order.shipments.map(&:to_package)
|
|
210
|
+
@differentiator = Spree::Stock::Differentiator.new(@order, packages)
|
|
211
|
+
@differentiator.missing.each do |variant, quantity|
|
|
212
|
+
@order.contents.remove(variant, quantity)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
if try_spree_current_user && try_spree_current_user.respond_to?(:wallet)
|
|
217
|
+
@wallet_payment_sources = try_spree_current_user.wallet.wallet_payment_sources
|
|
218
|
+
@default_wallet_payment_source = @wallet_payment_sources.detect(&:default) ||
|
|
219
|
+
@wallet_payment_sources.first
|
|
220
|
+
# TODO: How can we deprecate this instance variable? We could try
|
|
221
|
+
# wrapping it in a delegating object that produces deprecation warnings.
|
|
222
|
+
@payment_sources = try_spree_current_user.wallet.wallet_payment_sources.map(&:payment_source).select { |ps| ps.is_a?(Spree::CreditCard) }
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def rescue_from_spree_gateway_error(exception)
|
|
227
|
+
flash.now[:error] = t('spree.spree_gateway_error_flash_for_checkout')
|
|
228
|
+
@order.errors.add(:base, exception.message)
|
|
229
|
+
render :edit
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def check_authorization
|
|
233
|
+
authorize!(:edit, current_order, cookies.signed[:guest_token])
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class HomeController < Spree::StoreController
|
|
5
|
+
helper 'spree/products'
|
|
6
|
+
respond_to :html
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
@searcher = build_searcher(params.merge(include_images: true))
|
|
10
|
+
@products = @searcher.retrieve_products
|
|
11
|
+
@taxonomies = Spree::Taxonomy.includes(root: :children)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class LocaleController < Spree::StoreController
|
|
5
|
+
def set
|
|
6
|
+
available_locales = Spree.i18n_available_locales
|
|
7
|
+
requested_locale = params[:switch_to_locale] || params[:locale]
|
|
8
|
+
|
|
9
|
+
if requested_locale && available_locales.map(&:to_s).include?(requested_locale)
|
|
10
|
+
session[:locale] = requested_locale
|
|
11
|
+
I18n.locale = requested_locale
|
|
12
|
+
flash.notice = t('spree.locale_changed')
|
|
13
|
+
else
|
|
14
|
+
flash[:error] = t('spree.locale_not_changed')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
redirect_to spree.root_path
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class OrdersController < Spree::StoreController
|
|
5
|
+
helper 'spree/products', 'spree/orders'
|
|
6
|
+
|
|
7
|
+
respond_to :html
|
|
8
|
+
|
|
9
|
+
before_action :store_guest_token
|
|
10
|
+
before_action :assign_order, only: :update
|
|
11
|
+
# note: do not lock the #edit action because that's where we redirect when we fail to acquire a lock
|
|
12
|
+
around_action :lock_order, only: :update
|
|
13
|
+
before_action :apply_coupon_code, only: :update
|
|
14
|
+
skip_before_action :verify_authenticity_token, only: [:populate]
|
|
15
|
+
|
|
16
|
+
def show
|
|
17
|
+
@order = Spree::Order.find_by!(number: params[:id])
|
|
18
|
+
authorize! :read, @order, cookies.signed[:guest_token]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def update
|
|
22
|
+
authorize! :update, @order, cookies.signed[:guest_token]
|
|
23
|
+
if @order.contents.update_cart(order_params)
|
|
24
|
+
@order.next if params.key?(:checkout) && @order.cart?
|
|
25
|
+
|
|
26
|
+
respond_with(@order) do |format|
|
|
27
|
+
format.html do
|
|
28
|
+
if params.key?(:checkout)
|
|
29
|
+
redirect_to checkout_state_path(@order.checkout_steps.first)
|
|
30
|
+
else
|
|
31
|
+
redirect_to cart_path
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
respond_with(@order)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Shows the current incomplete order from the session
|
|
41
|
+
def edit
|
|
42
|
+
@order = current_order || Spree::Order.incomplete.find_or_initialize_by(guest_token: cookies.signed[:guest_token])
|
|
43
|
+
authorize! :read, @order, cookies.signed[:guest_token]
|
|
44
|
+
associate_user
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Adds a new item to the order (creating a new order if none already exists)
|
|
48
|
+
def populate
|
|
49
|
+
@order = current_order(create_order_if_necessary: true)
|
|
50
|
+
authorize! :update, @order, cookies.signed[:guest_token]
|
|
51
|
+
|
|
52
|
+
variant = Spree::Variant.find(params[:variant_id])
|
|
53
|
+
quantity = params[:quantity].present? ? params[:quantity].to_i : 1
|
|
54
|
+
|
|
55
|
+
# 2,147,483,647 is crazy. See issue https://github.com/spree/spree/issues/2695.
|
|
56
|
+
if !quantity.between?(1, 2_147_483_647)
|
|
57
|
+
@order.errors.add(:base, t('spree.please_enter_reasonable_quantity'))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
begin
|
|
61
|
+
@line_item = @order.contents.add(variant, quantity)
|
|
62
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
63
|
+
@order.errors.add(:base, e.record.errors.full_messages.join(", "))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
respond_with(@order) do |format|
|
|
67
|
+
format.html do
|
|
68
|
+
if @order.errors.any?
|
|
69
|
+
flash[:error] = @order.errors.full_messages.join(", ")
|
|
70
|
+
redirect_back_or_default(spree.root_path)
|
|
71
|
+
return
|
|
72
|
+
else
|
|
73
|
+
redirect_to cart_path
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def populate_redirect
|
|
80
|
+
flash[:error] = t('spree.populate_get_error')
|
|
81
|
+
redirect_to spree.cart_path
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def empty
|
|
85
|
+
if @order = current_order
|
|
86
|
+
authorize! :update, @order, cookies.signed[:guest_token]
|
|
87
|
+
@order.empty!
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
redirect_to spree.cart_path
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def accurate_title
|
|
94
|
+
if @order && @order.completed?
|
|
95
|
+
t('spree.order_number', number: @order.number)
|
|
96
|
+
else
|
|
97
|
+
t('spree.shopping_cart')
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def store_guest_token
|
|
104
|
+
cookies.permanent.signed[:guest_token] = params[:token] if params[:token]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def order_params
|
|
108
|
+
if params[:order]
|
|
109
|
+
params[:order].permit(*permitted_order_attributes)
|
|
110
|
+
else
|
|
111
|
+
{}
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def assign_order
|
|
116
|
+
@order = current_order
|
|
117
|
+
unless @order
|
|
118
|
+
flash[:error] = t('spree.order_not_found')
|
|
119
|
+
redirect_to(root_path) && return
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def apply_coupon_code
|
|
124
|
+
if order_params[:coupon_code].present?
|
|
125
|
+
@order.coupon_code = order_params[:coupon_code]
|
|
126
|
+
|
|
127
|
+
handler = PromotionHandler::Coupon.new(@order).apply
|
|
128
|
+
|
|
129
|
+
if handler.error.present?
|
|
130
|
+
flash.now[:error] = handler.error
|
|
131
|
+
respond_with(@order) { |format| format.html { render :edit } } && return
|
|
132
|
+
elsif handler.success
|
|
133
|
+
flash[:success] = handler.success
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class ProductsController < Spree::StoreController
|
|
5
|
+
before_action :load_product, only: :show
|
|
6
|
+
before_action :load_taxon, only: :index
|
|
7
|
+
|
|
8
|
+
helper 'spree/taxons', 'spree/taxon_filters'
|
|
9
|
+
|
|
10
|
+
respond_to :html
|
|
11
|
+
|
|
12
|
+
def index
|
|
13
|
+
@searcher = build_searcher(params.merge(include_images: true))
|
|
14
|
+
@products = @searcher.retrieve_products
|
|
15
|
+
@taxonomies = Spree::Taxonomy.includes(root: :children)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def show
|
|
19
|
+
@variants = @product.
|
|
20
|
+
variants_including_master.
|
|
21
|
+
display_includes.
|
|
22
|
+
with_prices(current_pricing_options).
|
|
23
|
+
includes([:option_values, :images])
|
|
24
|
+
|
|
25
|
+
@product_properties = @product.product_properties.includes(:property)
|
|
26
|
+
@taxon = Spree::Taxon.find(params[:taxon_id]) if params[:taxon_id]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def accurate_title
|
|
32
|
+
if @product
|
|
33
|
+
@product.meta_title.blank? ? @product.name : @product.meta_title
|
|
34
|
+
else
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def load_product
|
|
40
|
+
if try_spree_current_user.try(:has_spree_role?, "admin")
|
|
41
|
+
@products = Spree::Product.with_deleted
|
|
42
|
+
else
|
|
43
|
+
@products = Spree::Product.available
|
|
44
|
+
end
|
|
45
|
+
@product = @products.friendly.find(params[:id])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def load_taxon
|
|
49
|
+
@taxon = Spree::Taxon.find(params[:taxon]) if params[:taxon].present?
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class StoreController < Spree::BaseController
|
|
5
|
+
include Spree::Core::ControllerHelpers::Pricing
|
|
6
|
+
include Spree::Core::ControllerHelpers::Order
|
|
7
|
+
|
|
8
|
+
def unauthorized
|
|
9
|
+
render 'spree/shared/unauthorized', layout: Spree::Config[:layout], status: 401
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def cart_link
|
|
13
|
+
render partial: 'spree/shared/link_to_cart'
|
|
14
|
+
fresh_when(current_order, template: 'spree/shared/_link_to_cart')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def config_locale
|
|
20
|
+
Spree::Frontend::Config[:locale]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def lock_order
|
|
24
|
+
Spree::OrderMutex.with_lock!(@order) { yield }
|
|
25
|
+
rescue Spree::OrderMutex::LockFailed
|
|
26
|
+
flash[:error] = t('spree.order_mutex_error')
|
|
27
|
+
redirect_to spree.cart_path
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class TaxonsController < Spree::StoreController
|
|
5
|
+
helper 'spree/products', 'spree/taxon_filters'
|
|
6
|
+
|
|
7
|
+
before_action :load_taxon, only: [:show]
|
|
8
|
+
|
|
9
|
+
respond_to :html
|
|
10
|
+
|
|
11
|
+
def show
|
|
12
|
+
@searcher = build_searcher(params.merge(taxon: @taxon.id, include_images: true))
|
|
13
|
+
@products = @searcher.retrieve_products
|
|
14
|
+
@taxonomies = Spree::Taxonomy.includes(root: :children)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def load_taxon
|
|
20
|
+
@taxon = Spree::Taxon.find_by!(permalink: params[:id])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def accurate_title
|
|
24
|
+
if @taxon
|
|
25
|
+
@taxon.seo_title
|
|
26
|
+
else
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'truncate_html'
|
|
4
|
+
require 'app/helpers/truncate_html_helper'
|
|
5
|
+
|
|
6
|
+
module Spree
|
|
7
|
+
module OrdersHelper
|
|
8
|
+
include TruncateHtmlHelper
|
|
9
|
+
|
|
10
|
+
def truncated_product_description(product)
|
|
11
|
+
truncate_html(raw(product.description))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def order_just_completed?(order)
|
|
15
|
+
flash[:order_completed] && order.present?
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spree/core/product_filters'
|
|
4
|
+
|
|
5
|
+
module Spree
|
|
6
|
+
module TaxonFiltersHelper
|
|
7
|
+
def applicable_filters_for(_taxon)
|
|
8
|
+
[:brand_filter, :price_filter].map do |filter_name|
|
|
9
|
+
Spree::Core::ProductFilters.send(filter_name) if Spree::Core::ProductFilters.respond_to?(filter_name)
|
|
10
|
+
end.compact
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class FrontendConfiguration < Preferences::Configuration
|
|
5
|
+
preference :locale, :string, default: Rails.application.config.i18n.default_locale
|
|
6
|
+
|
|
7
|
+
# Add your terms and conditions in app/views/spree/checkout/_terms_and_conditions.en.html.erb
|
|
8
|
+
preference :require_terms_and_conditions_acceptance, :boolean, default: false
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<% address_id = address_type.chars.first %>
|
|
2
|
+
<div class="inner" data-hook=<%="#{address_type}_inner" %>>
|
|
3
|
+
<div class="field field-required" id=<%="#{address_id}firstname" %>>
|
|
4
|
+
<%= form.label :firstname, t('spree.first_name') %>
|
|
5
|
+
<%= form.text_field :firstname, class: 'required', autocomplete: address_type + ' given-name', required: true, autofocus: true %>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="field" id=<%="#{address_id}lastname" %>>
|
|
9
|
+
<%= form.label :lastname, t('spree.last_name') %>
|
|
10
|
+
<%= form.text_field :lastname, autocomplete: address_type + ' family-name' %>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<% if Spree::Config[:company] %>
|
|
14
|
+
<div class="field" id=<%="#{address_id}company" %>>
|
|
15
|
+
<%= form.label :company, t('spree.company') %>
|
|
16
|
+
<%= form.text_field :company, autocomplete: address_type + ' organization' %>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<div class="field field-required" id=<%="#{address_id}address1" %>>
|
|
21
|
+
<%= form.label :address1, t('spree.street_address') %>
|
|
22
|
+
<%= form.text_field :address1, class: 'required', autocomplete: address_type + ' address-line1', required: true %>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="field" id=<%="#{address_id}address2" %>>
|
|
26
|
+
<%= form.label :address2, I18n.t('spree.street_address_2') %>
|
|
27
|
+
<%= form.text_field :address2, autocomplete: address_type + ' address-line2' %>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="field field-required" id=<%="#{address_id}city" %>>
|
|
31
|
+
<%= form.label :city, t('spree.city') %>
|
|
32
|
+
<%= form.text_field :city, class: 'required', autocomplete: address_type + ' address-level2', required: true %>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div class="field field-required" id=<%="#{address_id}country" %>>
|
|
36
|
+
<%= form.label :country_id, t('spree.country') %>
|
|
37
|
+
<span id=<%="#{address_id}country-selection" %>>
|
|
38
|
+
<%= form.collection_select :country_id, available_countries, :id, :name, {},
|
|
39
|
+
class: 'required',
|
|
40
|
+
autocomplete: address_type + ' country-name',
|
|
41
|
+
required: true
|
|
42
|
+
%>
|
|
43
|
+
</span>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<% if Spree::Config[:address_requires_state] %>
|
|
47
|
+
<div class="field field-required" id=<%="#{address_id}state" %>>
|
|
48
|
+
<% have_states = !address.country.states.empty? %>
|
|
49
|
+
<%= form.label :state, t('spree.state') %>
|
|
50
|
+
|
|
51
|
+
<span class="js-address-fields" style="display: none;">
|
|
52
|
+
<%=
|
|
53
|
+
form.collection_select(
|
|
54
|
+
:state_id, address.country.states, :id, :name,
|
|
55
|
+
{include_blank: true},
|
|
56
|
+
{
|
|
57
|
+
class: have_states ? 'required' : '',
|
|
58
|
+
style: have_states ? '' : 'display: none;',
|
|
59
|
+
disabled: !have_states,
|
|
60
|
+
autocomplete: address_type + ' address-level1',
|
|
61
|
+
})
|
|
62
|
+
%>
|
|
63
|
+
<%=
|
|
64
|
+
form.text_field(
|
|
65
|
+
:state_name,
|
|
66
|
+
class: !have_states ? 'required' : '',
|
|
67
|
+
style: have_states ? 'display: none;' : '',
|
|
68
|
+
disabled: have_states,
|
|
69
|
+
autocomplete: address_type + ' address-level1',
|
|
70
|
+
)
|
|
71
|
+
%>
|
|
72
|
+
</span>
|
|
73
|
+
<noscript>
|
|
74
|
+
<%= form.text_field :state_name, class: 'required', autocomplete: address_type + ' address-level1', required: true %>
|
|
75
|
+
</noscript>
|
|
76
|
+
</div>
|
|
77
|
+
<% end %>
|
|
78
|
+
|
|
79
|
+
<div class="field <%= 'field-required' if address.require_zipcode? %>" id=<%="#{address_id}zipcode" %>>
|
|
80
|
+
<%= form.label :zipcode, t('spree.zip') %>
|
|
81
|
+
<%= form.text_field :zipcode, class: "#{'required' if address.require_zipcode?}", autocomplete: address_type + ' postal-code', required: true %>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="field <%= 'field-required' if address.require_phone? %>" id=<%="#{address_id}phone" %>>
|
|
85
|
+
<%= form.label :phone, t('spree.phone') %>
|
|
86
|
+
<% phone_hash = address.require_phone? ? { class: 'required', required: true } : {} %>
|
|
87
|
+
<%= form.phone_field :phone, phone_hash.merge({ autocomplete: address_type + ' home tel' }) %>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<% if Spree::Config[:alternative_shipping_phone] %>
|
|
91
|
+
<div class="field" id=<%="#{address_id}altphone" %>>
|
|
92
|
+
<%= form.label :alternative_phone, t('spree.alternative_phone') %>
|
|
93
|
+
<%= form.phone_field :alternative_phone, autocomplete: address_type + ' tel'%>
|
|
94
|
+
</div>
|
|
95
|
+
<% end %>
|
|
96
|
+
</div>
|