# # Controller for managing orders, i.e. all actions that require the "orders" role. # Normal ordering actions of members of order groups is handled by the OrderingController. class OrdersController < ApplicationController include Concerns::SendOrderPdf include SepaHelper before_action :authenticate_pickups_or_orders before_action :authenticate_orders, except: %i[receive receive_on_order_article_create receive_on_order_article_update show] before_action :remove_empty_article, only: %i[create update] # List orders def index @open_orders = Order.open.includes(:supplier) @finished_orders = Order.finished_not_closed.includes(:supplier) @per_page = 15 sort = if params['sort'] case params['sort'] when 'supplier' then 'suppliers.name, ends DESC' when 'pickup' then 'pickup DESC' when 'ends' then 'ends DESC' when 'supplier_reverse' then 'suppliers.name DESC' when 'ends_reverse' then 'ends' end else 'ends DESC' end @suppliers = Supplier.having_articles.order('suppliers.name') @orders = Order.closed.includes(:supplier).reorder(sort).page(params[:page]).per(@per_page) end # Gives a view for the results to a specific order # Renders also the pdf def show @order = Order.find(params[:id]) @view = (params[:view] || 'default').gsub(/[^-_a-zA-Z0-9]/, '') @partial = case @view when 'default' then 'articles' when 'groups' then 'shared/articles_by/groups' when 'articles' then 'shared/articles_by/articles' else 'articles' end respond_to do |format| format.html format.js do render layout: false end format.pdf do send_order_pdf @order, params[:document] end format.csv do send_data OrderCsv.new(@order).to_csv, filename: @order.name + '.csv', type: 'text/csv' end format.text do send_data OrderTxt.new(@order).to_txt, filename: @order.name + '.txt', type: 'text/plain' end end end # Page to create a new order. def new if params[:order_id] old_order = Order.find(params[:order_id]) @order = Order.new(supplier_id: old_order.supplier_id).init_dates @order.article_ids = old_order.article_ids else @order = Order.new(supplier_id: params[:supplier_id]).init_dates end rescue StandardError => e redirect_to orders_url, alert: t('errors.general_msg', msg: e.message) end # Page to edit an exsiting order. # editing finished orders is done in FinanceController def edit @order = Order.includes(:articles).find(params[:id]) end # Save a new order. # order_articles will be saved in Order.article_ids=() def create @order = Order.new(params[:order]) @order.created_by = current_user @order.updated_by = current_user if @order.save flash[:notice] = I18n.t('orders.create.notice') redirect_to @order else logger.debug "[debug] order errors: #{@order.errors.messages}" render action: 'new' end end # Update an existing order. def update @order = Order.find params[:id] if @order.update(params[:order].merge(updated_by: current_user)) flash[:notice] = I18n.t('orders.update.notice') redirect_to action: 'show', id: @order else render action: 'edit' end end # Delete an order. def destroy Order.find(params[:id]).destroy redirect_to action: 'index' end # Finish a current order. def finish order = Order.find(params[:id]) order.finish!(@current_user) redirect_to order, notice: I18n.t('orders.finish.notice') rescue StandardError => e redirect_to orders_url, alert: I18n.t('errors.general_msg', msg: e.message) end # Send a order to the supplier. def send_result_to_supplier order = Order.find(params[:id]) order.send_to_supplier!(@current_user) redirect_to order, notice: I18n.t('orders.send_to_supplier.notice') rescue StandardError => e redirect_to order, alert: I18n.t('errors.general_msg', msg: e.message) end def receive @order = Order.find(params[:id]) if request.post? Order.transaction do s = update_order_amounts @order.update_attribute(:state, 'received') if @order.state != 'received' flash[:notice] = (s ? I18n.t('orders.receive.notice', msg: s) : I18n.t('orders.receive.notice_none')) end NotifyReceivedOrderJob.perform_later(@order) if current_user.role_orders? || current_user.role_finance? redirect_to @order elsif current_user.role_pickups? redirect_to pickups_path else redirect_to receive_order_path(@order) end else @order_articles = @order.order_articles.ordered_or_member.includes(:article).order('articles.order_number, articles.name') end end def receive_on_order_article_create # See publish/subscribe design pattern in /doc. @order_article = OrderArticle.find(params[:order_article_id]) render layout: false end def receive_on_order_article_update # See publish/subscribe design pattern in /doc. @order_article = OrderArticle.find(params[:order_article_id]) render layout: false end def collective_direct_debit if foodsoft_sepa_ready? case params[:mode] when 'all' group_orders = GroupOrder.where(order_id: params[:id]) when 'selected' group_orders = GroupOrder.where(id: params[:group_order_ids]) else redirect_to finance_order_index_path, alert: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: '') end @order = Order.find(params[:id]) ordergroups = group_orders.map(&:ordergroup) export_allowed = !ordergroups.map(&:sepa_possible?).include?(false) && !group_orders.map { |go| go.group_order_invoice.present? }.include?(false) group_order_ids = group_orders.map { |go| go.id if go.group_order_invoice.present? } sepa_possible_ordergroup_names = ordergroups.map { |ordergroup| ordergroup.name if ordergroup.sepa_possible? }.compact_blank sepa_not_possible_ordergroup_names = ordergroups.map(&:name) - sepa_possible_ordergroup_names if export_allowed && group_orders.present? respond_to do |format| format.html do collective_debit = OrderCollectiveDirectDebitXml.new(group_orders) send_data collective_debit.xml_string, filename: @order.name + '_Sammellastschrift' + '.xml', type: 'text/xml' group_orders.map(&:group_order_invoice).each(&:mark_sepa_downloaded) rescue SEPA::Error => e group_orders.map(&:group_order_invoice).each(&:unmark_sepa_downloaded) redirect_to finance_order_index_path, alert: e.message rescue StandardError => e group_orders.map(&:group_order_invoice).each(&:unmark_sepa_downloaded) redirect_to finance_order_index_path, alert: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_not_possible_ordergroup_names.join(', '), error: e.message) end format.xml do group_orders.map(&:group_order_invoice).each(&:mark_sepa_downloaded) collective_debit = OrderCollectiveDirectDebitXml.new(group_orders) send_data collective_debit.xml_string, filename: @order.name + '_Sammellastschrift' + '.xml', type: 'text/xml' rescue SEPA::Error => e group_orders.map(&:group_order_invoice).each(&:unmark_sepa_downloaded) render json: { error: e.message } rescue StandardError => e group_orders.map(&:group_order_invoice).each(&:unmark_sepa_downloaded) render json: { error: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_not_possible_ordergroup_names.join(', '), error: e.message) } end format.js end else respond_to do |format| format.html do redirect_to finance_order_index_path, alert: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_not_possible_ordergroup_names.join(', '), error: '') end format.xml do render json: { error: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_not_possible_ordergroup_names.join(', '), error: '') } end format.js end end else respond_to do |format| format.html do redirect_to finance_order_index_path, alert: "Wichtige SEPA Konfiguration in Administration >> Einstellungen >> Finanzen nicht gesetzt!" end format.xml do redirect_to finance_order_index_path, alert: "Wichtige SEPA Konfiguration in Administration >> Einstellungen >> Finanzen nicht gesetzt!" end format.js end end end protected def update_order_amounts return unless params[:order_articles] # where to leave remainder during redistribution rest_to = [] rest_to << :tolerance if params[:rest_to_tolerance] rest_to << :stock if params[:rest_to_stock] rest_to << nil # count what happens to the articles: # changed, rest_to_tolerance, rest_to_stock, left_over counts = [0] * 4 cunits = [0] * 4 # This was once wrapped in a transaction, but caused # "MySQL lock timeout exceeded" errors. It's ok to do # this article-by-article anway. params[:order_articles].each do |oa_id, oa_params| next if oa_params.blank? oa = OrderArticle.find(oa_id) # update attributes; don't use update_attribute because it calls save # which makes received_changed? not work anymore oa.attributes = oa_params if oa.units_received_changed? counts[0] += 1 if oa.units_received.present? cunits[0] += oa.units_received * oa.article.unit_quantity oacounts = oa.redistribute oa.units_received * oa.price.unit_quantity, rest_to oacounts.each_with_index do |c, i| cunits[i + 1] += c counts[i + 1] += 1 if c > 0 end end end oa.save! end return nil if counts[0] == 0 notice = [] notice << I18n.t('orders.update_order_amounts.msg1', count: counts[0], units: cunits[0]) if params[:rest_to_tolerance] notice << I18n.t('orders.update_order_amounts.msg2', count: counts[1], units: cunits[1]) end notice << I18n.t('orders.update_order_amounts.msg3', count: counts[2], units: cunits[2]) if params[:rest_to_stock] if counts[3] > 0 || cunits[3] > 0 notice << I18n.t('orders.update_order_amounts.msg4', count: counts[3], units: cunits[3]) end notice.join(', ') end def remove_empty_article params[:order][:article_ids].compact_blank! if params[:order] && params[:order][:article_ids] end end