mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Move BulkCoop reports out of deprecated lib/ directory into OrderManagement engine.
The BulkCoop reports are not generated the same way as the EnterpriseFeeSummary report is generated yet so that may need to be updated.
This commit is contained in:
@@ -12,7 +12,6 @@ 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'
|
||||
|
||||
@@ -21,6 +20,11 @@ module Spree
|
||||
class ReportsController < Spree::Admin::BaseController
|
||||
include Spree::ReportsHelper
|
||||
|
||||
ORDER_MANAGEMENT_ENGINE_REPORTS = [
|
||||
:bulk_coop,
|
||||
:enterprise_fee_summary
|
||||
]
|
||||
|
||||
helper_method :render_content?
|
||||
|
||||
before_filter :cache_search_state
|
||||
@@ -309,7 +313,7 @@ module Spree
|
||||
|
||||
# List of reports that have been moved to the Order Management engine
|
||||
def report_in_order_management_engine?(report)
|
||||
report == :enterprise_fee_summary
|
||||
ORDER_MANAGEMENT_ENGINE_REPORTS.include?(report)
|
||||
end
|
||||
|
||||
def timestamp
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
= form_for @report.search, :url => spree.bulk_coop_admin_reports_path do |f|
|
||||
= render 'date_range_form', f: f
|
||||
|
||||
.row
|
||||
.four.columns.alpha
|
||||
= label_tag nil, t(:report_distributor)
|
||||
= f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => t(:all)}, {:class => "select2 fullwidth"})
|
||||
= label_tag nil, t(:report_type)
|
||||
%br
|
||||
= select_tag(:report_type, options_for_select([:bulk_coop_supplier_report, :bulk_coop_allocation, :bulk_coop_packing_sheets, :bulk_coop_customer_payments].map{ |e| [t(".#{e}"), e] }, @report_type))
|
||||
%br
|
||||
%br
|
||||
= check_box_tag :csv
|
||||
= label_tag :csv, t(:report_customers_csv)
|
||||
%br
|
||||
%br
|
||||
= button t(:search)
|
||||
|
||||
= render "table", id: "listing_orders", msg_option: t(:search)
|
||||
@@ -0,0 +1,64 @@
|
||||
module OrderManagement
|
||||
module Reports
|
||||
class BulkCoopController < Spree::Admin::BaseController
|
||||
before_filter :load_report_parameters
|
||||
before_filter :load_permissions
|
||||
|
||||
def new; end
|
||||
|
||||
def create
|
||||
return respond_to_invalid_parameters unless @report_parameters.valid?
|
||||
|
||||
@report_parameters.authorize!(@permissions)
|
||||
|
||||
@report = report_klass::ReportService.new(@permissions, params[:report], spree_current_user)
|
||||
renderer.render(self)
|
||||
rescue ::Reports::Authorizer::ParameterNotAllowedError => e
|
||||
flash[:error] = e.message
|
||||
render_report_form
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def respond_to_invalid_parameters
|
||||
flash[:error] = I18n.t("invalid_filter_parameters", scope: i18n_scope)
|
||||
render_report_form
|
||||
end
|
||||
|
||||
def i18n_scope
|
||||
"order_management.reports.enterprise_fee_summary"
|
||||
end
|
||||
|
||||
def render_report_form
|
||||
render action: :new
|
||||
end
|
||||
|
||||
def report_klass
|
||||
OrderManagement::Reports::BulkCoop
|
||||
end
|
||||
|
||||
def load_report_parameters
|
||||
@report_parameters = report_klass::Parameters.new(params[:report] || {})
|
||||
end
|
||||
|
||||
def load_permissions
|
||||
@permissions = report_klass::Permissions.new(spree_current_user)
|
||||
end
|
||||
|
||||
def report_renderer_klass
|
||||
case params[:report_format]
|
||||
when "csv"
|
||||
report_klass::Renderers::CsvRenderer
|
||||
when nil, "", "html"
|
||||
report_klass::Renderers::HtmlRenderer
|
||||
else
|
||||
raise Reports::UnsupportedReportFormatException
|
||||
end
|
||||
end
|
||||
|
||||
def renderer
|
||||
@renderer ||= report_renderer_klass.new(@report)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
module OrderManagement
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class Authorizer < ::Reports::Authorizer
|
||||
def self.parameter_not_allowed_error_message
|
||||
i18n_scope = "order_management.reports.enterprise_fee_summary"
|
||||
I18n.t("parameter_not_allowed_error", scope: i18n_scope)
|
||||
end
|
||||
|
||||
def authorize!
|
||||
require_ids_allowed(parameters.distributor_ids, permissions.allowed_distributors)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,67 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderManagement
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class BulkCoopAllocationReport
|
||||
def header
|
||||
[
|
||||
I18n.t(:report_header_customer),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_bulk_unit_size),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_variant_value),
|
||||
I18n.t(:report_header_variant_unit),
|
||||
I18n.t(:report_header_weight),
|
||||
I18n.t(:report_header_sum_total),
|
||||
I18n.t(:report_header_total_available),
|
||||
I18n.t(:report_header_unallocated),
|
||||
I18n.t(:report_header_max_quantity_excess),
|
||||
]
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [
|
||||
:total_label,
|
||||
:variant_product_name,
|
||||
:variant_product_group_buy_unit_size_f,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:total_amount,
|
||||
:total_available,
|
||||
:remainder,
|
||||
:max_quantity_excess
|
||||
]
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order },
|
||||
sort_by: proc { |order| order.to_s }
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def columns
|
||||
[
|
||||
:order_billing_address_name,
|
||||
:product_name,
|
||||
:product_group_buy_unit_size,
|
||||
:full_name,
|
||||
:option_value_value,
|
||||
:option_value_unit,
|
||||
:weight_from_unit_value,
|
||||
:total_amount,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,275 @@
|
||||
require "open_food_network/reports/line_items"
|
||||
|
||||
module OrderManagement
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class BulkCoopReport
|
||||
REPORT_TYPES = [
|
||||
:bulk_coop_supplier_report,
|
||||
:bulk_coop_allocation,
|
||||
:bulk_coop_packing_sheets,
|
||||
:bulk_coop_customer_payments
|
||||
]
|
||||
|
||||
attr_reader :params
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@params = params
|
||||
@user = user
|
||||
@render_table = render_table
|
||||
|
||||
@supplier_report = BulkCoopSupplierReport.new
|
||||
@allocation_report = BulkCoopAllocationReport.new
|
||||
end
|
||||
|
||||
def header
|
||||
case params[:report_type]
|
||||
when "bulk_coop_supplier_report"
|
||||
@supplier_report.header
|
||||
when "bulk_coop_allocation"
|
||||
@allocation_report.header
|
||||
when "bulk_coop_packing_sheets"
|
||||
[I18n.t(:report_header_customer),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_sum_total)]
|
||||
when "bulk_coop_customer_payments"
|
||||
[I18n.t(:report_header_customer),
|
||||
I18n.t(:report_header_date_of_order),
|
||||
I18n.t(:report_header_total_cost),
|
||||
I18n.t(:report_header_amount_owing),
|
||||
I18n.t(:report_header_amount_paid)]
|
||||
else
|
||||
[I18n.t(:report_header_supplier),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_bulk_unit_size),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_weight),
|
||||
I18n.t(:report_header_sum_total),
|
||||
I18n.t(:report_header_sum_max_total),
|
||||
I18n.t(:report_header_units_required),
|
||||
I18n.t(:report_header_remainder)]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
report_line_items.orders
|
||||
end
|
||||
|
||||
def table_items
|
||||
return [] unless @render_table
|
||||
report_line_items.list(line_item_includes)
|
||||
end
|
||||
|
||||
def rules
|
||||
case params[:report_type]
|
||||
when "bulk_coop_supplier_report"
|
||||
@supplier_report.rules
|
||||
when "bulk_coop_allocation"
|
||||
@allocation_report.rules
|
||||
when "bulk_coop_packing_sheets"
|
||||
[{ group_by: proc { |li| li.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |li| li.full_name },
|
||||
sort_by: proc { |full_name| full_name } },
|
||||
{ group_by: proc { |li| li.order },
|
||||
sort_by: proc { |order| order.to_s } }]
|
||||
when "bulk_coop_customer_payments"
|
||||
[{ group_by: proc { |li| li.order },
|
||||
sort_by: proc { |order| order.completed_at } }]
|
||||
else
|
||||
[{ group_by: proc { |li| li.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |li| li.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [proc { |lis| lis.first.product.supplier.name },
|
||||
proc { |lis| lis.first.product.name },
|
||||
proc { |lis| lis.first.product.group_buy_unit_size || 0.0 },
|
||||
proc { |_lis| "" },
|
||||
proc { |_lis| "" },
|
||||
proc { |lis| lis.sum { |li| li.quantity * (li.weight_from_unit_value || 0) } },
|
||||
proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.weight_from_unit_value || 0) } },
|
||||
proc { |lis| ( (lis.first.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| [li.max_quantity || 0, li.quantity || 0].max * (li.weight_from_unit_value || 0) } / lis.first.product.group_buy_unit_size ) ).floor },
|
||||
proc { |lis| lis.sum { |li| [li.max_quantity || 0, li.quantity || 0].max * (li.weight_from_unit_value || 0) } - ( ( (lis.first.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| [li.max_quantity || 0, li.quantity || 0].max * (li.weight_from_unit_value || 0) } / lis.first.product.group_buy_unit_size ) ).floor * (lis.first.product.group_buy_unit_size || 0) ) }] },
|
||||
{ group_by: proc { |li| li.full_name },
|
||||
sort_by: proc { |full_name| full_name } }]
|
||||
end
|
||||
end
|
||||
|
||||
def columns
|
||||
case params[:report_type]
|
||||
when "bulk_coop_supplier_report"
|
||||
@supplier_report.columns
|
||||
when "bulk_coop_allocation"
|
||||
@allocation_report.columns
|
||||
when "bulk_coop_packing_sheets"
|
||||
[
|
||||
:order_billing_address_name,
|
||||
:product_name,
|
||||
:full_name,
|
||||
:total_quantity
|
||||
]
|
||||
when "bulk_coop_customer_payments"
|
||||
[
|
||||
:order_billing_address_name,
|
||||
:order_completed_at,
|
||||
:customer_payments_total_cost,
|
||||
:customer_payments_amount_owed,
|
||||
:customer_payments_amount_paid
|
||||
]
|
||||
else
|
||||
[
|
||||
:product_supplier_name,
|
||||
:product_name,
|
||||
:product_group_buy_unit_size,
|
||||
:full_name,
|
||||
:weight_from_unit_value,
|
||||
:total_quantity,
|
||||
:total_max_quantity,
|
||||
:empty_cell,
|
||||
:empty_cell
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def line_item_includes
|
||||
[{ order: [:bill_address],
|
||||
variant: [{ option_values: :option_type }, { product: :supplier }] }]
|
||||
end
|
||||
|
||||
def order_permissions
|
||||
return @order_permissions unless @order_permissions.nil?
|
||||
@order_permissions = ::Permissions::Order.new(@user, @params[:q])
|
||||
end
|
||||
|
||||
def report_line_items
|
||||
@report_line_items ||= OpenFoodNetwork::Reports::LineItems.new(order_permissions, @params)
|
||||
end
|
||||
|
||||
def customer_payments_total_cost(line_items)
|
||||
line_items.map(&:order).uniq.sum(&:total)
|
||||
end
|
||||
|
||||
def customer_payments_amount_owed(line_items)
|
||||
line_items.map(&:order).uniq.sum(&:outstanding_balance)
|
||||
end
|
||||
|
||||
def customer_payments_amount_paid(line_items)
|
||||
line_items.map(&:order).uniq.sum(&:payment_total)
|
||||
end
|
||||
|
||||
def empty_cell(*)
|
||||
""
|
||||
end
|
||||
|
||||
def full_name(line_items)
|
||||
line_items.first.full_name
|
||||
end
|
||||
|
||||
def group_buy_unit_size(line_items)
|
||||
(line_items.first.variant.product.group_buy_unit_size || 0.0) /
|
||||
(line_items.first.product.variant_unit_scale || 1)
|
||||
end
|
||||
|
||||
def max_quantity_excess(line_items)
|
||||
max_quantity_amount(line_items) - total_amount(line_items)
|
||||
end
|
||||
|
||||
def max_quantity_amount(line_items)
|
||||
line_items.sum do |line_item|
|
||||
max_quantity = [line_item.max_quantity || 0, line_item.quantity || 0].max
|
||||
max_quantity * scaled_unit_value(line_item.variant)
|
||||
end
|
||||
end
|
||||
|
||||
def option_value_value(line_items)
|
||||
OpenFoodNetwork::OptionValueNamer.new(line_items.first).value
|
||||
end
|
||||
|
||||
def option_value_unit(line_items)
|
||||
OpenFoodNetwork::OptionValueNamer.new(line_items.first).unit
|
||||
end
|
||||
|
||||
def order_billing_address_name(line_items)
|
||||
billing_address = line_items.first.order.bill_address
|
||||
billing_address.firstname + " " + billing_address.lastname
|
||||
end
|
||||
|
||||
def order_completed_at(line_items)
|
||||
line_items.first.order.completed_at.to_s
|
||||
end
|
||||
|
||||
def product_group_buy_unit_size(line_items)
|
||||
line_items.first.product.group_buy_unit_size || 0.0
|
||||
end
|
||||
|
||||
def product_name(line_items)
|
||||
line_items.first.product.name
|
||||
end
|
||||
|
||||
def product_supplier_name(line_items)
|
||||
line_items.first.product.supplier.name
|
||||
end
|
||||
|
||||
def remainder(line_items)
|
||||
remainder = total_available(line_items) - total_amount(line_items)
|
||||
remainder >= 0 ? remainder : ''
|
||||
end
|
||||
|
||||
def scaled_final_weight_volume(line_item)
|
||||
(line_item.final_weight_volume || 0) / (line_item.product.variant_unit_scale || 1)
|
||||
end
|
||||
|
||||
def scaled_unit_value(variant)
|
||||
(variant.unit_value || 0) / (variant.product.variant_unit_scale || 1)
|
||||
end
|
||||
|
||||
def total_amount(line_items)
|
||||
line_items.sum { |li| scaled_final_weight_volume(li) }
|
||||
end
|
||||
|
||||
def total_available(line_items)
|
||||
units_required(line_items) * group_buy_unit_size(line_items)
|
||||
end
|
||||
|
||||
def total_max_quantity(line_items)
|
||||
line_items.sum { |line_item| line_item.max_quantity || 0 }
|
||||
end
|
||||
|
||||
def total_quantity(line_items)
|
||||
line_items.sum(&:quantity)
|
||||
end
|
||||
|
||||
def total_label(*)
|
||||
I18n.t('admin.reports.total')
|
||||
end
|
||||
|
||||
def units_required(line_items)
|
||||
if group_buy_unit_size(line_items).zero?
|
||||
0
|
||||
else
|
||||
( total_amount(line_items) / group_buy_unit_size(line_items) ).ceil
|
||||
end
|
||||
end
|
||||
|
||||
def variant_product_group_buy_unit_size_f(line_items)
|
||||
group_buy_unit_size(line_items)
|
||||
end
|
||||
|
||||
def variant_product_name(line_items)
|
||||
line_items.first.variant.product.name
|
||||
end
|
||||
|
||||
def variant_product_supplier_name(line_items)
|
||||
line_items.first.variant.product.supplier.name
|
||||
end
|
||||
|
||||
def weight_from_unit_value(line_items)
|
||||
line_items.first.weight_from_unit_value || 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,64 @@
|
||||
module OrderManagement
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class BulkCoopSupplierReport
|
||||
def header
|
||||
[
|
||||
I18n.t(:report_header_supplier),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_bulk_unit_size),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_variant_value),
|
||||
I18n.t(:report_header_variant_unit),
|
||||
I18n.t(:report_header_weight),
|
||||
I18n.t(:report_header_sum_total),
|
||||
I18n.t(:report_header_units_required),
|
||||
I18n.t(:report_header_unallocated),
|
||||
I18n.t(:report_header_max_quantity_excess),
|
||||
]
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{ group_by: proc { |line_item| line_item.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [
|
||||
:variant_product_supplier_name,
|
||||
:variant_product_name,
|
||||
:variant_product_group_buy_unit_size_f,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:total_amount,
|
||||
:units_required,
|
||||
:remainder,
|
||||
:max_quantity_excess
|
||||
]
|
||||
},
|
||||
{ group_by: proc { |line_item| line_item.full_name },
|
||||
sort_by: proc { |full_name| full_name } }
|
||||
]
|
||||
end
|
||||
|
||||
def columns
|
||||
[
|
||||
:variant_product_supplier_name,
|
||||
:variant_product_name,
|
||||
:variant_product_group_buy_unit_size_f,
|
||||
:full_name,
|
||||
:option_value_value,
|
||||
:option_value_unit,
|
||||
:weight_from_unit_value,
|
||||
:total_amount,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
module OrderManagement
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class Parameters < ::Reports::Parameters::Base
|
||||
extend ActiveModel::Naming
|
||||
extend ActiveModel::Translation
|
||||
include ActiveModel::Validations
|
||||
|
||||
attr_accessor :start_at, :end_at, :distributor_ids, :report_type
|
||||
|
||||
before_validation :cleanup_arrays
|
||||
|
||||
validates :start_at, :end_at, date_time_string: true
|
||||
validates :distributor_ids, integer_array: true
|
||||
validates_inclusion_of :report_type, in: OrderManagement::Reports::BulkCoop::BulkCoopReport::REPORT_TYPES.map(&:to_s)
|
||||
|
||||
validate :require_valid_datetime_range
|
||||
|
||||
def self.date_end_before_start_error_message
|
||||
i18n_scope = "order_management.reports.enterprise_fee_summary"
|
||||
I18n.t("date_end_before_start_error", scope: i18n_scope)
|
||||
end
|
||||
|
||||
def initialize(attributes = {})
|
||||
self.distributor_ids = []
|
||||
|
||||
super(attributes)
|
||||
end
|
||||
|
||||
def authorize!(permissions)
|
||||
authorizer = Authorizer.new(self, permissions)
|
||||
authorizer.authorize!
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def require_valid_datetime_range
|
||||
return if start_at.blank? || end_at.blank?
|
||||
|
||||
error_message = self.class.date_end_before_start_error_message
|
||||
errors.add(:end_at, error_message) unless start_at < end_at
|
||||
end
|
||||
|
||||
# Remove the blank strings that Rails multiple selects add by default to
|
||||
# make sure that blank lists are still submitted to the server as arrays
|
||||
# instead of nil.
|
||||
#
|
||||
# https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-select
|
||||
def cleanup_arrays
|
||||
distributor_ids.reject!(&:blank?)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
module OrderManagement
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class Permissions < ::Reports::Permissions
|
||||
def allowed_distributors
|
||||
@allowed_distributors ||= Enterprise.is_distributor.managed_by(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
module OrderManagement
|
||||
module Reports
|
||||
module BulkCoop
|
||||
module Renderers
|
||||
class CsvRenderer < ::Reports::Renderers::Base
|
||||
def render(context)
|
||||
context.send_data(generate, filename: filename)
|
||||
end
|
||||
|
||||
def generate
|
||||
CSV.generate do |csv|
|
||||
csv << report_data.header
|
||||
|
||||
report_data.list.each do |data|
|
||||
csv << data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filename
|
||||
timestamp = Time.zone.now.strftime("%Y%m%d")
|
||||
"#{report_data.parameters[:report_type]}_#{timestamp}.csv"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
module OrderManagement
|
||||
module Reports
|
||||
module BulkCoop
|
||||
module Renderers
|
||||
class HtmlRenderer < ::Reports::Renderers::Base
|
||||
def render(context)
|
||||
context.instance_variable_set :@renderer, self
|
||||
context.render(action: :create, renderer: self)
|
||||
end
|
||||
|
||||
def header
|
||||
report_data.header
|
||||
end
|
||||
|
||||
def data_rows
|
||||
report_data.list
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
require 'open_food_network/order_grouper'
|
||||
|
||||
module OrderManagement
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class ReportService
|
||||
attr_accessor :permissions, :parameters, :user
|
||||
|
||||
def initialize(permissions, parameters, user)
|
||||
@permissions = permissions
|
||||
@parameters = parameters
|
||||
@user = user
|
||||
@report = BulkCoopReport.new(user, parameters, true)
|
||||
end
|
||||
|
||||
def header
|
||||
@report.header
|
||||
end
|
||||
|
||||
def list
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns, @report
|
||||
order_grouper.table(@report.table_items)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,11 +2,6 @@ module OrderManagement
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
class Authorizer < ::Reports::Authorizer
|
||||
def self.parameter_not_allowed_error_message
|
||||
i18n_scope = "order_management.reports.enterprise_fee_summary"
|
||||
I18n.t("parameter_not_allowed_error", scope: i18n_scope)
|
||||
end
|
||||
|
||||
def authorize!
|
||||
authorize_by_distribution!
|
||||
authorize_by_fee!
|
||||
@@ -25,14 +20,6 @@ module OrderManagement
|
||||
require_ids_allowed(parameters.shipping_method_ids, permissions.allowed_shipping_methods)
|
||||
require_ids_allowed(parameters.payment_method_ids, permissions.allowed_payment_methods)
|
||||
end
|
||||
|
||||
def require_ids_allowed(array, allowed_objects)
|
||||
error_klass = ::Reports::Authorizer::ParameterNotAllowedError
|
||||
error_message = self.class.parameter_not_allowed_error_message
|
||||
ids_allowed = (array - allowed_objects.map(&:id).map(&:to_s)).blank?
|
||||
|
||||
raise error_klass, error_message unless ids_allowed
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,5 +8,20 @@ module Reports
|
||||
@parameters = parameters
|
||||
@permissions = permissions
|
||||
end
|
||||
|
||||
def self.parameter_not_allowed_error_message
|
||||
i18n_scope = "order_management.reports.shared"
|
||||
I18n.t("parameter_not_allowed_error", scope: i18n_scope)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_ids_allowed(array, allowed_objects)
|
||||
error_klass = ::Reports::Authorizer::ParameterNotAllowedError
|
||||
error_message = self.class.parameter_not_allowed_error_message
|
||||
ids_allowed = (array - allowed_objects.map(&:id).map(&:to_s)).blank?
|
||||
|
||||
raise error_klass, error_message unless ids_allowed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
= form_for @report_parameters, as: :report, url: main_app.order_management_reports_bulk_coop_path, method: :post do |f|
|
||||
.row.date-range-filter
|
||||
.sixteen.columns.alpha
|
||||
= label_tag nil, t(".date_range")
|
||||
%br
|
||||
|
||||
= f.label :start_at, class: "inline"
|
||||
= f.text_field :start_at, class: "datetimepicker datepicker-from"
|
||||
|
||||
%span.range-divider
|
||||
%i.icon-arrow-right
|
||||
|
||||
= f.text_field :end_at, class: "datetimepicker datepicker-to"
|
||||
= f.label :end_at, class: "inline"
|
||||
|
||||
.row
|
||||
.sixteen.columns.alpha
|
||||
= f.label :distributor_ids
|
||||
= f.collection_select(:distributor_ids, @permissions.allowed_distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
|
||||
|
||||
.row
|
||||
.sixteen.columns.alpha
|
||||
= f.label :report_type
|
||||
= f.collection_select(:report_type, OrderManagement::Reports::BulkCoop::BulkCoopReport::REPORT_TYPES.map { |report_type| [t(".#{report_type}"), report_type] }, :last, :first, {}, {class: "select2 fullwidth", multiple: false})
|
||||
|
||||
.row
|
||||
.sixteen.columns.alpha
|
||||
= check_box_tag :report_format, "csv", false, id: "report_format_csv"
|
||||
= label_tag :report_format_csv, t(".report_format_csv")
|
||||
|
||||
= button t(".generate_report")
|
||||
@@ -0,0 +1,20 @@
|
||||
- if @report.present?
|
||||
%table#bulk_coop_report.report__table
|
||||
%thead
|
||||
%tr
|
||||
- @renderer.header.each do |heading|
|
||||
%th= heading
|
||||
|
||||
%tbody
|
||||
- @renderer.data_rows.each do |row|
|
||||
%tr
|
||||
- row.each do |cell_value|
|
||||
%td= cell_value
|
||||
|
||||
- if @renderer.data_rows.empty?
|
||||
%tr
|
||||
%td{colspan: @renderer.header.length}= t('.none')
|
||||
- else
|
||||
%p.report__message
|
||||
= t(".select_and_search")
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
= render "filters"
|
||||
= render "report"
|
||||
@@ -0,0 +1 @@
|
||||
= render "filters"
|
||||
@@ -1,6 +1,7 @@
|
||||
Openfoodnetwork::Application.routes.prepend do
|
||||
namespace :order_management do
|
||||
namespace :reports do
|
||||
resource :bulk_coop, only: [:new, :create], controller: :bulk_coop
|
||||
resource :enterprise_fee_summary, only: [:new, :create]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
require 'open_food_network/reports/bulk_coop_supplier_report'
|
||||
require 'open_food_network/reports/bulk_coop_allocation_report'
|
||||
require "open_food_network/reports/line_items"
|
||||
|
||||
module OpenFoodNetwork
|
||||
class BulkCoopReport
|
||||
attr_reader :params
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@params = params
|
||||
@user = user
|
||||
@render_table = render_table
|
||||
|
||||
@supplier_report = Reports::BulkCoopSupplierReport.new
|
||||
@allocation_report = Reports::BulkCoopAllocationReport.new
|
||||
end
|
||||
|
||||
def header
|
||||
case params[:report_type]
|
||||
when "bulk_coop_supplier_report"
|
||||
@supplier_report.header
|
||||
when "bulk_coop_allocation"
|
||||
@allocation_report.header
|
||||
when "bulk_coop_packing_sheets"
|
||||
[I18n.t(:report_header_customer),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_sum_total)]
|
||||
when "bulk_coop_customer_payments"
|
||||
[I18n.t(:report_header_customer),
|
||||
I18n.t(:report_header_date_of_order),
|
||||
I18n.t(:report_header_total_cost),
|
||||
I18n.t(:report_header_amount_owing),
|
||||
I18n.t(:report_header_amount_paid)]
|
||||
else
|
||||
[I18n.t(:report_header_supplier),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_bulk_unit_size),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_weight),
|
||||
I18n.t(:report_header_sum_total),
|
||||
I18n.t(:report_header_sum_max_total),
|
||||
I18n.t(:report_header_units_required),
|
||||
I18n.t(:report_header_remainder)]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
report_line_items.orders
|
||||
end
|
||||
|
||||
def table_items
|
||||
return [] unless @render_table
|
||||
report_line_items.list(line_item_includes)
|
||||
end
|
||||
|
||||
def rules
|
||||
case params[:report_type]
|
||||
when "bulk_coop_supplier_report"
|
||||
@supplier_report.rules
|
||||
when "bulk_coop_allocation"
|
||||
@allocation_report.rules
|
||||
when "bulk_coop_packing_sheets"
|
||||
[{ group_by: proc { |li| li.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |li| li.full_name },
|
||||
sort_by: proc { |full_name| full_name } },
|
||||
{ group_by: proc { |li| li.order },
|
||||
sort_by: proc { |order| order.to_s } }]
|
||||
when "bulk_coop_customer_payments"
|
||||
[{ group_by: proc { |li| li.order },
|
||||
sort_by: proc { |order| order.completed_at } }]
|
||||
else
|
||||
[{ group_by: proc { |li| li.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |li| li.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [proc { |lis| lis.first.product.supplier.name },
|
||||
proc { |lis| lis.first.product.name },
|
||||
proc { |lis| lis.first.product.group_buy_unit_size || 0.0 },
|
||||
proc { |_lis| "" },
|
||||
proc { |_lis| "" },
|
||||
proc { |lis| lis.sum { |li| li.quantity * (li.weight_from_unit_value || 0) } },
|
||||
proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.weight_from_unit_value || 0) } },
|
||||
proc { |lis| ( (lis.first.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| [li.max_quantity || 0, li.quantity || 0].max * (li.weight_from_unit_value || 0) } / lis.first.product.group_buy_unit_size ) ).floor },
|
||||
proc { |lis| lis.sum { |li| [li.max_quantity || 0, li.quantity || 0].max * (li.weight_from_unit_value || 0) } - ( ( (lis.first.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| [li.max_quantity || 0, li.quantity || 0].max * (li.weight_from_unit_value || 0) } / lis.first.product.group_buy_unit_size ) ).floor * (lis.first.product.group_buy_unit_size || 0) ) }] },
|
||||
{ group_by: proc { |li| li.full_name },
|
||||
sort_by: proc { |full_name| full_name } }]
|
||||
end
|
||||
end
|
||||
|
||||
def columns
|
||||
case params[:report_type]
|
||||
when "bulk_coop_supplier_report"
|
||||
@supplier_report.columns
|
||||
when "bulk_coop_allocation"
|
||||
@allocation_report.columns
|
||||
when "bulk_coop_packing_sheets"
|
||||
[
|
||||
:order_billing_address_name,
|
||||
:product_name,
|
||||
:full_name,
|
||||
:total_quantity
|
||||
]
|
||||
when "bulk_coop_customer_payments"
|
||||
[
|
||||
:order_billing_address_name,
|
||||
:order_completed_at,
|
||||
:customer_payments_total_cost,
|
||||
:customer_payments_amount_owed,
|
||||
:customer_payments_amount_paid
|
||||
]
|
||||
else
|
||||
[
|
||||
:product_supplier_name,
|
||||
:product_name,
|
||||
:product_group_buy_unit_size,
|
||||
:full_name,
|
||||
:weight_from_unit_value,
|
||||
:total_quantity,
|
||||
:total_max_quantity,
|
||||
:empty_cell,
|
||||
:empty_cell
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def line_item_includes
|
||||
[{ order: [:bill_address],
|
||||
variant: [{ option_values: :option_type }, { product: :supplier }] }]
|
||||
end
|
||||
|
||||
def order_permissions
|
||||
return @order_permissions unless @order_permissions.nil?
|
||||
@order_permissions = ::Permissions::Order.new(@user, @params[:q])
|
||||
end
|
||||
|
||||
def report_line_items
|
||||
@report_line_items ||= Reports::LineItems.new(order_permissions, @params)
|
||||
end
|
||||
|
||||
def customer_payments_total_cost(line_items)
|
||||
line_items.map(&:order).uniq.sum(&:total)
|
||||
end
|
||||
|
||||
def customer_payments_amount_owed(line_items)
|
||||
line_items.map(&:order).uniq.sum(&:outstanding_balance)
|
||||
end
|
||||
|
||||
def customer_payments_amount_paid(line_items)
|
||||
line_items.map(&:order).uniq.sum(&:payment_total)
|
||||
end
|
||||
|
||||
def empty_cell(*)
|
||||
""
|
||||
end
|
||||
|
||||
def full_name(line_items)
|
||||
line_items.first.full_name
|
||||
end
|
||||
|
||||
def group_buy_unit_size(line_items)
|
||||
(line_items.first.variant.product.group_buy_unit_size || 0.0) /
|
||||
(line_items.first.product.variant_unit_scale || 1)
|
||||
end
|
||||
|
||||
def max_quantity_excess(line_items)
|
||||
max_quantity_amount(line_items) - total_amount(line_items)
|
||||
end
|
||||
|
||||
def max_quantity_amount(line_items)
|
||||
line_items.sum do |line_item|
|
||||
max_quantity = [line_item.max_quantity || 0, line_item.quantity || 0].max
|
||||
max_quantity * scaled_unit_value(line_item.variant)
|
||||
end
|
||||
end
|
||||
|
||||
def option_value_value(line_items)
|
||||
OpenFoodNetwork::OptionValueNamer.new(line_items.first).value
|
||||
end
|
||||
|
||||
def option_value_unit(line_items)
|
||||
OpenFoodNetwork::OptionValueNamer.new(line_items.first).unit
|
||||
end
|
||||
|
||||
def order_billing_address_name(line_items)
|
||||
billing_address = line_items.first.order.bill_address
|
||||
billing_address.firstname + " " + billing_address.lastname
|
||||
end
|
||||
|
||||
def order_completed_at(line_items)
|
||||
line_items.first.order.completed_at.to_s
|
||||
end
|
||||
|
||||
def product_group_buy_unit_size(line_items)
|
||||
line_items.first.product.group_buy_unit_size || 0.0
|
||||
end
|
||||
|
||||
def product_name(line_items)
|
||||
line_items.first.product.name
|
||||
end
|
||||
|
||||
def product_supplier_name(line_items)
|
||||
line_items.first.product.supplier.name
|
||||
end
|
||||
|
||||
def remainder(line_items)
|
||||
remainder = total_available(line_items) - total_amount(line_items)
|
||||
remainder >= 0 ? remainder : ''
|
||||
end
|
||||
|
||||
def scaled_final_weight_volume(line_item)
|
||||
(line_item.final_weight_volume || 0) / (line_item.product.variant_unit_scale || 1)
|
||||
end
|
||||
|
||||
def scaled_unit_value(variant)
|
||||
(variant.unit_value || 0) / (variant.product.variant_unit_scale || 1)
|
||||
end
|
||||
|
||||
def total_amount(line_items)
|
||||
line_items.sum { |li| scaled_final_weight_volume(li) }
|
||||
end
|
||||
|
||||
def total_available(line_items)
|
||||
units_required(line_items) * group_buy_unit_size(line_items)
|
||||
end
|
||||
|
||||
def total_max_quantity(line_items)
|
||||
line_items.sum { |line_item| line_item.max_quantity || 0 }
|
||||
end
|
||||
|
||||
def total_quantity(line_items)
|
||||
line_items.sum(&:quantity)
|
||||
end
|
||||
|
||||
def total_label(*)
|
||||
I18n.t('admin.reports.total')
|
||||
end
|
||||
|
||||
def units_required(line_items)
|
||||
if group_buy_unit_size(line_items).zero?
|
||||
0
|
||||
else
|
||||
( total_amount(line_items) / group_buy_unit_size(line_items) ).ceil
|
||||
end
|
||||
end
|
||||
|
||||
def variant_product_group_buy_unit_size_f(line_items)
|
||||
group_buy_unit_size(line_items)
|
||||
end
|
||||
|
||||
def variant_product_name(line_items)
|
||||
line_items.first.variant.product.name
|
||||
end
|
||||
|
||||
def variant_product_supplier_name(line_items)
|
||||
line_items.first.variant.product.supplier.name
|
||||
end
|
||||
|
||||
def weight_from_unit_value(line_items)
|
||||
line_items.first.weight_from_unit_value || 0
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,63 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork::Reports
|
||||
class BulkCoopAllocationReport
|
||||
def header
|
||||
[
|
||||
I18n.t(:report_header_customer),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_bulk_unit_size),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_variant_value),
|
||||
I18n.t(:report_header_variant_unit),
|
||||
I18n.t(:report_header_weight),
|
||||
I18n.t(:report_header_sum_total),
|
||||
I18n.t(:report_header_total_available),
|
||||
I18n.t(:report_header_unallocated),
|
||||
I18n.t(:report_header_max_quantity_excess),
|
||||
]
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [
|
||||
:total_label,
|
||||
:variant_product_name,
|
||||
:variant_product_group_buy_unit_size_f,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:total_amount,
|
||||
:total_available,
|
||||
:remainder,
|
||||
:max_quantity_excess
|
||||
]
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order },
|
||||
sort_by: proc { |order| order.to_s }
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def columns
|
||||
[
|
||||
:order_billing_address_name,
|
||||
:product_name,
|
||||
:product_group_buy_unit_size,
|
||||
:full_name,
|
||||
:option_value_value,
|
||||
:option_value_unit,
|
||||
:weight_from_unit_value,
|
||||
:total_amount,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,60 +0,0 @@
|
||||
module OpenFoodNetwork::Reports
|
||||
class BulkCoopSupplierReport
|
||||
def header
|
||||
[
|
||||
I18n.t(:report_header_supplier),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_bulk_unit_size),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_variant_value),
|
||||
I18n.t(:report_header_variant_unit),
|
||||
I18n.t(:report_header_weight),
|
||||
I18n.t(:report_header_sum_total),
|
||||
I18n.t(:report_header_units_required),
|
||||
I18n.t(:report_header_unallocated),
|
||||
I18n.t(:report_header_max_quantity_excess),
|
||||
]
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{ group_by: proc { |line_item| line_item.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [
|
||||
:variant_product_supplier_name,
|
||||
:variant_product_name,
|
||||
:variant_product_group_buy_unit_size_f,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:total_amount,
|
||||
:units_required,
|
||||
:remainder,
|
||||
:max_quantity_excess
|
||||
]
|
||||
},
|
||||
{ group_by: proc { |line_item| line_item.full_name },
|
||||
sort_by: proc { |full_name| full_name } }
|
||||
]
|
||||
end
|
||||
|
||||
def columns
|
||||
[
|
||||
:variant_product_supplier_name,
|
||||
:variant_product_name,
|
||||
:variant_product_group_buy_unit_size_f,
|
||||
:full_name,
|
||||
:option_value_value,
|
||||
:option_value_unit,
|
||||
:weight_from_unit_value,
|
||||
:total_amount,
|
||||
:empty_cell,
|
||||
:empty_cell,
|
||||
:empty_cell
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user