foodsoft/app/controllers/orders_controller.rb
2023-11-17 15:53:32 +01:00

282 lines
10 KiB
Ruby

#
# 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 unless ordergroup.sepa_possible? }.compact_blank
if export_allowed && group_orders.present?
respond_to do |format|
format.html 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 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_possible_ordergroup_names.join(', '))
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: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_possible_ordergroup_names.join(', ')) }
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_possible_ordergroup_names.join(', '))
end
format.xml do
render json: { error: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_possible_ordergroup_names.join(', ')) }
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