diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 011f30f66c..6d2a56baaa 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -360,8 +360,8 @@ Metrics/AbcSize: - app/controllers/enterprises_controller.rb - app/controllers/spree/admin/adjustments_controller_decorator.rb - app/controllers/spree/admin/image_settings_controller.rb - - app/controllers/spree/admin/orders/customer_details_controller_decorator.rb - - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/admin/orders/customer_details_controller.rb + - app/controllers/spree/admin/orders_controller.rb - app/controllers/spree/admin/overview_controller.rb - app/controllers/spree/admin/payment_methods_controller.rb - app/controllers/spree/admin/payments_controller.rb @@ -565,7 +565,8 @@ Metrics/MethodLength: - app/controllers/checkout_controller.rb - app/controllers/shop_controller.rb - app/controllers/spree/admin/image_settings_controller.rb - - app/controllers/spree/admin/orders/customer_details_controller_decorator.rb + - app/controllers/spree/admin/orders/customer_details_controller.rb + - app/controllers/spree/admin/orders_controller.rb - app/controllers/spree/admin/payment_methods_controller.rb - app/controllers/spree/admin/payments_controller.rb - app/controllers/spree/admin/reports_controller.rb @@ -652,6 +653,7 @@ Metrics/ClassLength: - app/controllers/api/products_controller.rb - app/controllers/checkout_controller.rb - app/controllers/spree/admin/base_controller.rb + - app/controllers/spree/admin/orders_controller.rb - app/controllers/spree/admin/payment_methods_controller.rb - app/controllers/spree/admin/reports_controller.rb - app/controllers/spree/admin/resource_controller.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index bc0d0d90c4..33970eb805 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -335,6 +335,7 @@ Rails/OutputSafety: - 'app/helpers/spree/admin/zones_helper.rb' - 'app/helpers/spree/reports_helper.rb' - 'app/helpers/spree/admin/navigation_helper.rb' + - 'app/helpers/spree/admin/orders_helper.rb' - 'app/serializers/api/product_serializer.rb' - 'lib/spree/money_decorator.rb' - 'spec/features/admin/orders_spec.rb' diff --git a/app/controllers/spree/admin/orders/customer_details_controller.rb b/app/controllers/spree/admin/orders/customer_details_controller.rb new file mode 100644 index 0000000000..2a22c1e086 --- /dev/null +++ b/app/controllers/spree/admin/orders/customer_details_controller.rb @@ -0,0 +1,71 @@ +module Spree + module Admin + module Orders + class CustomerDetailsController < Spree::Admin::BaseController + before_filter :load_order + before_filter :check_authorization + before_filter :set_guest_checkout_status, only: :update + + def show + edit + render action: :edit + end + + def edit + country_id = Address.default.country.id + @order.build_bill_address(country_id: country_id) if @order.bill_address.nil? + @order.build_ship_address(country_id: country_id) if @order.ship_address.nil? + end + + def update + if @order.update_attributes(params[:order]) + if params[:guest_checkout] == "false" + @order.associate_user!(Spree.user_class.find_by_email(@order.email)) + end + + AdvanceOrderService.new(@order).call + + @order.shipments.map(&:refresh_rates) + flash[:success] = Spree.t('customer_details_updated') + redirect_to admin_order_customer_path(@order) + else + render action: :edit + end + end + + # Inherit CanCan permissions for the current order + def model_class + load_order unless @order + @order + end + + private + + def load_order + @order = Order.find_by_number!(params[:order_id], include: :adjustments) + end + + def check_authorization + load_order + session[:access_token] ||= params[:token] + + resource = @order + action = params[:action].to_sym + action = :edit if action == :show # show route renders :edit for this controller + + authorize! action, resource, session[:access_token] + end + + def set_guest_checkout_status + registered_user = Spree::User.find_by_email(params[:order][:email]) + + params[:order][:guest_checkout] = registered_user.nil? + + return unless registered_user + + @order.user_id = registered_user.id + end + end + end + end +end diff --git a/app/controllers/spree/admin/orders/customer_details_controller_decorator.rb b/app/controllers/spree/admin/orders/customer_details_controller_decorator.rb deleted file mode 100644 index 996e9a72fb..0000000000 --- a/app/controllers/spree/admin/orders/customer_details_controller_decorator.rb +++ /dev/null @@ -1,49 +0,0 @@ -Spree::Admin::Orders::CustomerDetailsController.class_eval do - before_filter :check_authorization - before_filter :set_guest_checkout_status, only: :update - - def update - if @order.update_attributes(params[:order]) - if params[:guest_checkout] == "false" - @order.associate_user!(Spree.user_class.find_by_email(@order.email)) - end - - AdvanceOrderService.new(@order).call - - @order.shipments.map &:refresh_rates - flash[:success] = Spree.t('customer_details_updated') - redirect_to admin_order_customer_path(@order) - else - render action: :edit - end - end - - # Inherit CanCan permissions for the current order - def model_class - load_order unless @order - @order - end - - private - - def check_authorization - load_order - session[:access_token] ||= params[:token] - - resource = @order - action = params[:action].to_sym - action = :edit if action == :show # show route renders :edit for this controller - - authorize! action, resource, session[:access_token] - end - - def set_guest_checkout_status - registered_user = Spree::User.find_by_email(params[:order][:email]) - - params[:order][:guest_checkout] = registered_user.nil? - - return unless registered_user - - @order.user_id = registered_user.id - end -end diff --git a/app/controllers/spree/admin/orders_controller.rb b/app/controllers/spree/admin/orders_controller.rb new file mode 100644 index 0000000000..2ddc9dccc7 --- /dev/null +++ b/app/controllers/spree/admin/orders_controller.rb @@ -0,0 +1,150 @@ +require 'open_food_network/spree_api_key_loader' + +module Spree + module Admin + class OrdersController < Spree::Admin::BaseController + require 'spree/core/gateway_error' + include OpenFoodNetwork::SpreeApiKeyLoader + helper CheckoutHelper + + before_filter :load_order, only: [:edit, :update, :fire, :resend, + :invoice, :print, :print_ticket] + before_filter :load_distribution_choices, only: [:new, :edit, :update] + + # Ensure that the distributor is set for an order when + before_filter :ensure_distribution, only: :new + + # After updating an order, the fees should be updated as well + # Currently, adding or deleting line items does not trigger updating the + # fees! This is a quick fix for that. + # TODO: update fees when adding/removing line items + # instead of the update_distribution_charge method. + after_filter :update_distribution_charge, only: :update + + before_filter :require_distributor_abn, only: :invoice + + respond_to :html, :json + + def new + @order = Order.create + @order.created_by = try_spree_current_user + @order.save + redirect_to edit_admin_order_url(@order) + end + + def edit + @order.shipments.map(&:refresh_rates) + + AdvanceOrderService.new(@order).call + + # The payment step shows an error of 'No pending payments' + # Clearing the errors from the order object will stop this error + # appearing on the edit page where we don't want it to. + @order.errors.clear + end + + def update + unless @order.update_attributes(params[:order]) && @order.line_items.present? + if @order.line_items.empty? + @order.errors.add(:line_items, Spree.t('errors.messages.blank')) + end + return redirect_to(edit_admin_order_path(@order), + flash: { error: @order.errors.full_messages.join(', ') }) + end + + @order.update! + if @order.complete? + redirect_to edit_admin_order_path(@order) + else + # Jump to next step if order is not complete + redirect_to admin_order_customer_path(@order) + end + end + + def bulk_management + load_spree_api_key + end + + def fire + event = params[:e] + if @order.public_send(event.to_s) + flash[:success] = Spree.t(:order_updated) + else + flash[:error] = Spree.t(:cannot_perform_operation) + end + rescue Spree::Core::GatewayError => e + flash[:error] = e.message.to_s + ensure + redirect_to :back + end + + def resend + Spree::OrderMailer.confirm_email_for_customer(@order.id, true).deliver + flash[:success] = t(:order_email_resent) + + respond_with(@order) { |format| format.html { redirect_to :back } } + end + + def invoice + pdf = InvoiceRenderer.new.render_to_string(@order) + + Spree::OrderMailer.invoice_email(@order.id, pdf).deliver + flash[:success] = t('admin.orders.invoice_email_sent') + + respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } } + end + + def print + render InvoiceRenderer.new.args(@order) + end + + def print_ticket + render template: "spree/admin/orders/ticket", layout: false + end + + def update_distribution_charge + @order.update_distribution_charge! + end + + private + + def load_order + @order = Order.find_by_number!(params[:id], include: :adjustments) if params[:id] + authorize! action, @order + end + + def model_class + Spree::Order + end + + def require_distributor_abn + return if @order.distributor.abn.present? + + flash[:error] = t(:must_have_valid_business_number, + enterprise_name: @order.distributor.name) + respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } } + end + + def load_distribution_choices + @shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name + + ocs = OrderCycle.managed_by(spree_current_user) + @order_cycles = ocs.soonest_closing + + ocs.soonest_opening + + ocs.closed + + ocs.undated + end + + def ensure_distribution + unless @order + @order = Spree::Order.new + @order.generate_order_number + @order.save! + end + return if @order.distribution_set? + + render 'set_distribution', locals: { order: @order } + end + end + end +end diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb deleted file mode 100644 index 8483956024..0000000000 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ /dev/null @@ -1,120 +0,0 @@ -require 'open_food_network/spree_api_key_loader' - -Spree::Admin::OrdersController.class_eval do - include OpenFoodNetwork::SpreeApiKeyLoader - helper CheckoutHelper - before_filter :load_order, only: %i[show edit update fire resend invoice print print_ticket] - - before_filter :load_distribution_choices, only: [:new, :edit, :update] - - # Ensure that the distributor is set for an order when - before_filter :ensure_distribution, only: :new - - # After updating an order, the fees should be updated as well - # Currently, adding or deleting line items does not trigger updating the - # fees! This is a quick fix for that. - # TODO: update fees when adding/removing line items - # instead of the update_distribution_charge method. - after_filter :update_distribution_charge, only: :update - - before_filter :require_distributor_abn, only: :invoice - - respond_to :html, :json - - def index - # Overriding the action so we only render the page template. An angular request - # within the page then fetches the data it needs from Api::OrdersController - end - - def bulk_management - load_spree_api_key - end - - def edit - @order.shipments.map &:refresh_rates - - AdvanceOrderService.new(@order).call - - # The payment step shows an error of 'No pending payments' - # Clearing the errors from the order object will stop this error - # appearing on the edit page where we don't want it to. - @order.errors.clear - end - - # Re-implement spree method so that it redirects to edit instead of rendering edit - # This allows page reloads while adding variants to the order (/edit), without being redirected to customer details page (/update) - def update - unless @order.update_attributes(params[:order]) && @order.line_items.present? - @order.errors.add(:line_items, Spree.t('errors.messages.blank')) if @order.line_items.empty? - return redirect_to edit_admin_order_path(@order), flash: { error: @order.errors.full_messages.join(', ') } - end - - @order.update! - if @order.complete? - redirect_to edit_admin_order_path(@order) - else - # Jump to next step if order is not complete - redirect_to admin_order_customer_path(@order) - end - end - - # Overwrite to use confirm_email_for_customer instead of confirm_email. - # This uses a new template. See mailers/spree/order_mailer_decorator.rb. - def resend - Spree::OrderMailer.confirm_email_for_customer(@order.id, true).deliver - flash[:success] = t(:order_email_resent) - - respond_with(@order) { |format| format.html { redirect_to :back } } - end - - def invoice - pdf = InvoiceRenderer.new.render_to_string(@order) - - Spree::OrderMailer.invoice_email(@order.id, pdf).deliver - flash[:success] = t('admin.orders.invoice_email_sent') - - respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } } - end - - def print - render InvoiceRenderer.new.args(@order) - end - - def print_ticket - render template: "spree/admin/orders/ticket", layout: false - end - - def update_distribution_charge - @order.update_distribution_charge! - end - - private - - def require_distributor_abn - if @order.distributor.abn.blank? - flash[:error] = t(:must_have_valid_business_number, enterprise_name: @order.distributor.name) - respond_with(@order) { |format| format.html { redirect_to edit_admin_order_path(@order) } } - end - end - - def load_distribution_choices - @shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name - - ocs = OrderCycle.managed_by(spree_current_user) - @order_cycles = ocs.soonest_closing + - ocs.soonest_opening + - ocs.closed + - ocs.undated - end - - def ensure_distribution - unless @order - @order = Spree::Order.new - @order.generate_order_number - @order.save! - end - unless @order.distribution_set? - render 'set_distribution', locals: { order: @order } - end - end -end diff --git a/app/helpers/spree/admin/orders_helper_decorator.rb b/app/helpers/spree/admin/orders_helper.rb similarity index 80% rename from app/helpers/spree/admin/orders_helper_decorator.rb rename to app/helpers/spree/admin/orders_helper.rb index 0d62c2caa0..6f464f63fb 100644 --- a/app/helpers/spree/admin/orders_helper_decorator.rb +++ b/app/helpers/spree/admin/orders_helper.rb @@ -1,6 +1,17 @@ module Spree module Admin module OrdersHelper + def event_links + links = [] + links << event_link("cancel") if @order.can_cancel? + links << event_link("resume") if @order.can_resume? + links.join(' ').html_safe + end + + def line_item_shipment_price(line_item, quantity) + Spree::Money.new(line_item.price * quantity, currency: line_item.currency) + end + def order_links(order) @order ||= order links = [] @@ -100,6 +111,14 @@ module Spree icon: 'icon-trash', confirm: t(:are_you_sure) } end + + def event_link(event) + button_link_to(Spree.t(event), + fire_admin_order_url(@order, e: event), + method: :put, + icon: "icon-#{event}", + data: { confirm: Spree.t(:order_sure_want_to, event: Spree.t(event)) }) + end end end end diff --git a/app/views/spree/admin/shared/_address_form.html.erb b/app/views/spree/admin/shared/_address_form.html.erb new file mode 100644 index 0000000000..012c40d9e8 --- /dev/null +++ b/app/views/spree/admin/shared/_address_form.html.erb @@ -0,0 +1,72 @@ +<% if use_billing %> +
+ + <%= check_box_tag 'order[use_billing]', '1', (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address.same_as?(@order.ship_address)) %> + <%= label_tag 'order[use_billing]', Spree.t(:use_billing_address) %> + +
+<% end %> + +<% is_shipping_address = name == Spree.t(:shipping_address) %> +<% shipping_or_billing = is_shipping_address ? 'shipping' : 'billing' %> +<% s_or_b = is_shipping_address ? 's' : 'b' %> + +
+
"> + <%= f.label :firstname, Spree.t(:first_name) + ':' %> + <%= f.text_field :firstname, :class => 'fullwidth' %> +
+
"> + <%= f.label :lastname, Spree.t(:last_name) + ':' %> + <%= f.text_field :lastname, :class => 'fullwidth' %> +
+ <% if Spree::Config[:company] %> +
"> + <%= f.label :company, Spree.t(:company) + ':' %> + <%= f.text_field :company, :class => 'fullwidth' %> +
+ <% end %> +
"> + <%= f.label :address1, Spree.t(:street_address) + ':' %> + <%= f.text_field :address1, :class => 'fullwidth' %> +
+
"> + <%= f.label :address2, Spree.t(:street_address_2) + ':' %> + <%= f.text_field :address2, :class => 'fullwidth' %> +
+
"> + <%= f.label :city, Spree.t(:city) + ':' %> + <%= f.text_field :city, :class => 'fullwidth' %> +
+
"> + <%= f.label :zipcode, Spree.t(:zip) + ':' %> + <%= f.text_field :zipcode, :class => 'fullwidth' %> +
+
"> + <%= f.label :country_id, Spree.t(:country) + ':' %> + + <%= f.collection_select :country_id, available_countries, :id, :name, {}, {:class => 'select2 fullwidth'} %> + +
+
"> + <%= f.label :state_id, Spree.t(:state) + ':' %> + + <%= f.text_field :state_name, + :style => "display: #{f.object.country.states.empty? ? 'block' : 'none' };", + :disabled => !f.object.country.states.empty?, :class => 'fullwidth state_name' %> + <%= f.collection_select :state_id, f.object.country.states.sort, :id, :name, {:include_blank => true}, {:class => 'select2 fullwidth', :style => "display: #{f.object.country.states.empty? ? 'none' : 'block' };", :disabled => f.object.country.states.empty?} %> + +
+
"> + <%= f.label :phone, Spree.t(:phone) + ':' %> + <%= f.phone_field :phone, :class => 'fullwidth' %> +
+
+ +<% content_for :head do %> + <%= javascript_tag do -%> + $(document).ready(function(){ + $('span#<%= s_or_b %>country .select2').on('change', function() { update_state('<%= s_or_b %>'); }); + }); + <% end -%> +<% end %> diff --git a/app/views/spree/admin/shared/_update_order_state.js b/app/views/spree/admin/shared/_update_order_state.js new file mode 100644 index 0000000000..32b2d4d574 --- /dev/null +++ b/app/views/spree/admin/shared/_update_order_state.js @@ -0,0 +1,7 @@ +$('#order_tab_summary h5#order_status').html('<%= j Spree.t(:status) %>: <%= j Spree.t(@order.state, :scope => :order_state) %>'); +$('#order_tab_summary h5#order_total').html('<%= j Spree.t(:total) %>: <%= j @order.display_total.to_html %>'); + +<% if @order.completed? %> + $('#order_tab_summary h5#payment_status').html('<%= j Spree.t(:payment) %>: <%= j Spree.t(@order.payment_state, :scope => :payment_states, :default => [:missing, "none"]) %>'); + $('#order_tab_summary h5#shipment_status').html('<%= j Spree.t(:shipment) %>: <%= j Spree.t(@order.shipment_state, :scope => :shipment_state, :default => [:missing, "none"]) %>'); +<% end %> diff --git a/config/routes/spree.rb b/config/routes/spree.rb index e7fcf0d2cb..27f530b5cd 100644 --- a/config/routes/spree.rb +++ b/config/routes/spree.rb @@ -85,12 +85,18 @@ Spree::Core::Engine.routes.draw do get '/variants/search', :to => "variants#search", :as => :search_variants resources :orders do - get :invoice, on: :member - get :print, on: :member - get :print_ticket, on: :member - get :managed, on: :collection + member do + put :fire + get :fire + post :resend + get :invoice + get :print + get :print_ticket + end collection do + get :managed + resources :invoices, only: [:create, :show] do get :poll end @@ -103,6 +109,8 @@ Spree::Core::Engine.routes.draw do put :fire end end + + resource :customer, :controller => "orders/customer_details" end resources :users do