require 'csv' require 'open_food_network/reports/list' require 'open_food_network/order_and_distributor_report' require 'open_food_network/products_and_inventory_report' require 'open_food_network/lettuce_share_report' require 'open_food_network/group_buy_report' require 'open_food_network/order_grouper' require 'open_food_network/customers_report' require 'open_food_network/users_and_enterprises_report' require 'open_food_network/order_cycle_management_report' require 'open_food_network/packing_report' require 'open_food_network/sales_tax_report' require 'open_food_network/xero_invoices_report' require 'open_food_network/bulk_coop_report' require 'open_food_network/payments_report' require 'open_food_network/orders_and_fulfillments_report' module Spree module Admin class ReportsController < Spree::Admin::BaseController include Spree::ReportsHelper helper_method :render_content? before_filter :cache_search_state # Fetches user's distributors, suppliers and order_cycles before_filter :load_data, only: [:customers, :products_and_inventory, :order_cycle_management, :packing] respond_to :html def report_types OpenFoodNetwork::Reports::List.all end def index @reports = authorized_reports respond_with(@reports) end def customers @report_types = report_types[:customers] @report_type = params[:report_type] @report = OpenFoodNetwork::CustomersReport.new spree_current_user, params, render_content? render_report(@report.header, @report.table, params[:csv], "customers_#{timestamp}.csv") end def order_cycle_management params[:q] ||= {} @report_types = report_types[:order_cycle_management] @report_type = params[:report_type] # -- Build Report with Order Grouper @report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user, params, render_content? @table = @report.table_items render_report(@report.header, @table, params[:csv], "order_cycle_management_#{timestamp}.csv") end def packing params[:q] ||= {} @report_types = report_types[:packing] @report_type = params[:report_type] # -- Build Report with Order Grouper @report = OpenFoodNetwork::PackingReport.new spree_current_user, params, render_content? @table = order_grouper_table render_report(@report.header, @table, params[:csv], "packing_#{timestamp}.csv") end def orders_and_distributors @report = OpenFoodNetwork::OrderAndDistributorReport.new spree_current_user, params, render_content? @search = @report.search csv_file_name = "orders_and_distributors_#{timestamp}.csv" render_report(@report.header, @report.table, params[:csv], csv_file_name) end def sales_tax @distributors = my_distributors @report_type = params[:report_type] @report = OpenFoodNetwork::SalesTaxReport.new spree_current_user, params, render_content? render_report(@report.header, @report.table, params[:csv], "sales_tax.csv") end def bulk_coop # -- Prepare form options @distributors = my_distributors @report_type = params[:report_type] # -- Build Report with Order Grouper @report = OpenFoodNetwork::BulkCoopReport.new spree_current_user, params, render_content? @table = order_grouper_table csv_file_name = "bulk_coop_#{params[:report_type]}_#{timestamp}.csv" render_report(@report.header, @table, params[:csv], csv_file_name) end def payments # -- Prepare Form Options @distributors = my_distributors @report_type = params[:report_type] # -- Build Report with Order Grouper @report = OpenFoodNetwork::PaymentsReport.new spree_current_user, params, render_content? @table = order_grouper_table csv_file_name = "payments_#{timestamp}.csv" render_report(@report.header, @table, params[:csv], csv_file_name) end def orders_and_fulfillment params[:q] ||= orders_and_fulfillment_default_filters # -- Prepare Form Options permissions = OpenFoodNetwork::Permissions.new(spree_current_user) # My distributors and any distributors distributing products I supply @distributors = permissions.visible_enterprises_for_order_reports.is_distributor # My suppliers and any suppliers supplying products I distribute @suppliers = permissions.visible_enterprises_for_order_reports.is_primary_producer @order_cycles = my_order_cycles @report_types = report_types[:orders_and_fulfillment] @report_type = params[:report_type] @include_blank = I18n.t(:all) # -- Build Report with Order Grouper @report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user, params, render_content? @table = order_grouper_table csv_file_name = "#{params[:report_type]}_#{timestamp}.csv" render_report(@report.header, @table, params[:csv], csv_file_name) end def products_and_inventory @report_types = report_types[:products_and_inventory] @report = if params[:report_type] != 'lettuce_share' OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user, params, render_content? else OpenFoodNetwork::LettuceShareReport.new spree_current_user, params, render_content? end render_report @report.header, @report.table, params[:csv], "products_and_inventory_#{timestamp}.csv" end def users_and_enterprises @report = OpenFoodNetwork::UsersAndEnterprisesReport.new params, render_content? render_report(@report.header, @report.table, params[:csv], "users_and_enterprises_#{timestamp}.csv") end def xero_invoices params[:q] ||= {} @distributors = my_distributors @order_cycles = my_order_cycles @report = OpenFoodNetwork::XeroInvoicesReport.new(spree_current_user, params, render_content?) render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv") end private def model_class Spree::Admin::ReportsController end # Some actions are changing the `params` object. That is unfortunate Spree # behavior and we are building on it. So we have to look at `params` early # to check if we are searching or just displaying a report search form. def cache_search_state search_keys = [ # search parameter for ransack :q, # common in all reports, only set for CSV rendering :csv, # `button` is included in all forms. It's not important for searching, # but the Users & Enterprises report doesn't have any other parameter # for an empty search. So we use this one to display data. :button, # Some reports use filtering by enterprise or order cycle :distributor_id, :supplier_id, :order_cycle_id, # Xero Invoices can be filtered by date :invoice_date, :due_date ] @searching = search_keys.any? { |key| params.key? key } end # We don't want to render data unless search params are supplied. # Compiling data can take a long time. def render_content? @searching end def render_report(header, table, create_csv, csv_file_name) send_data csv_report(header, table), filename: csv_file_name if create_csv @header = header @table = table # Rendering HTML is the default. end def csv_report(header, table) CSV.generate do |csv| csv << header table.each { |row| csv << row } end end def load_data @distributors = my_distributors @suppliers = my_suppliers | suppliers_of_products_distributed_by(@distributors) @order_cycles = my_order_cycles end # Load managed distributor enterprises of current user def my_distributors Enterprise.is_distributor.managed_by(spree_current_user) end # Load managed producer enterprises of current user def my_suppliers Enterprise.is_primary_producer.managed_by(spree_current_user) end def suppliers_of_products_distributed_by(distributors) distributors.map { |d| Spree::Product.in_distributor(d).includes(:supplier).all }. flatten.map(&:supplier).uniq end # Load order cycles the current user has access to def my_order_cycles OrderCycle. active_or_complete. accessible_by(spree_current_user). order('orders_close_at DESC') end def order_grouper_table order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns order_grouper.table(@report.table_items) end def authorized_reports all_reports = [ :orders_and_distributors, :bulk_coop, :payments, :orders_and_fulfillment, :customers, :products_and_inventory, :users_and_enterprises, :enterprise_fee_summary, :order_cycle_management, :sales_tax, :xero_invoices, :packing ] reports = all_reports.select { |action| can? action, Spree::Admin::ReportsController } reports.map { |report| [report, describe_report(report)] }.to_h end def describe_report(report) name = I18n.t(:name, scope: [:admin, :reports, report]) description = begin I18n.t!(:description, scope: [:admin, :reports, report]) rescue I18n::MissingTranslationData render_to_string( partial: "#{report}_description", layout: false, locals: { report_types: report_types[report] } ).html_safe end { name: name, url: url_for_report(report), description: description } end def url_for_report(report) public_send("#{report}_admin_reports_url".to_sym) rescue NoMethodError url_for([:new, :admin, :reports, report.to_s.singularize]) end def timestamp Time.zone.now.strftime("%Y%m%d") end def orders_and_fulfillment_default_filters now = Time.zone.now { completed_at_gt: (now - 1.month).beginning_of_day, completed_at_lt: (now + 1.day).beginning_of_day } end end end end