Merge pull request #5526 from cillian/drop-blockenspiel

Drop blockenspiel
This commit is contained in:
Luis Ramos
2020-06-29 20:09:26 +01:00
committed by GitHub
42 changed files with 955 additions and 671 deletions

View File

@@ -81,7 +81,6 @@ gem "active_model_serializers", "0.8.4"
gem 'activerecord-session_store'
gem 'acts-as-taggable-on', '~> 4.0'
gem 'angularjs-file-upload-rails', '~> 2.4.1'
gem 'blockenspiel'
gem 'custom_error_message', github: 'jeremydurham/custom-err-msg'
gem 'dalli'
gem 'diffy'

View File

@@ -150,7 +150,6 @@ GEM
bcrypt (3.1.13)
bcrypt-ruby (3.1.5)
bcrypt (>= 3.1.3)
blockenspiel (0.5.0)
bugsnag (6.13.1)
concurrent-ruby (~> 1.0)
builder (3.1.4)
@@ -704,7 +703,6 @@ DEPENDENCIES
awesome_nested_set (~> 3.0.0.rc.1)
awesome_print
aws-sdk (= 1.11.1)
blockenspiel
bugsnag
byebug (~> 11.0.0)
cancan (~> 1.6.10)

View File

@@ -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
].freeze
helper_method :render_content?
before_action :cache_search_state
@@ -91,19 +95,6 @@ module Spree
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
@@ -262,7 +253,7 @@ module Spree
end
def order_grouper_table
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns, @report
order_grouper.table(@report.table_items)
end
@@ -311,7 +302,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

View File

@@ -185,9 +185,10 @@ class AbilityDecorator
can [:admin, :index, :guide, :import, :save, :save_data, :validate_data, :reset_absent_products], ProductImport::ProductImporter
# Reports page
can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments,
can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :payments,
:orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :packing],
Spree::Admin::ReportsController
add_bulk_coop_abilities
add_enterprise_fee_summary_abilities
end
@@ -264,9 +265,10 @@ class AbilityDecorator
end
# Reports page
can [:admin, :index, :customers, :group_buys, :bulk_coop, :sales_tax, :payments,
can [:admin, :index, :customers, :group_buys, :sales_tax, :payments,
:orders_and_distributors, :orders_and_fulfillment, :products_and_inventory,
:order_cycle_management, :xero_invoices], Spree::Admin::ReportsController
add_bulk_coop_abilities
add_enterprise_fee_summary_abilities
can [:create], Customer
@@ -291,6 +293,13 @@ class AbilityDecorator
end
end
def add_bulk_coop_abilities
# Reveal the report link in spree/admin/reports#index
can [:bulk_coop], Spree::Admin::ReportsController
# Allow direct access to the report resource
can [:admin, :new, :create], :bulk_coop
end
def add_enterprise_fee_summary_abilities
# Reveal the report link in spree/admin/reports#index
can [:enterprise_fee_summary], Spree::Admin::ReportsController

View File

@@ -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)

View File

@@ -0,0 +1,66 @@
# frozen_string_literal: true
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

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module BulkCoop
class Authorizer < ::Reports::Authorizer
def authorize!
require_ids_allowed(parameters.distributor_ids, permissions.allowed_distributors)
end
end
end
end
end

View File

@@ -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

View File

@@ -0,0 +1,279 @@
# frozen_string_literal: true
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
].freeze
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(_line_items)
""
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(_line_items)
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

View File

@@ -0,0 +1,65 @@
# frozen_string_literal: true
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

View File

@@ -0,0 +1,45 @@
# frozen_string_literal: true
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: BulkCoopReport::REPORT_TYPES.map(&:to_s)
validate :require_valid_datetime_range
def initialize(attributes = {})
self.distributor_ids = []
super(attributes)
end
def authorize!(permissions)
authorizer = Authorizer.new(self, permissions)
authorizer.authorize!
end
protected
# 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

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
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

View File

@@ -0,0 +1,32 @@
# frozen_string_literal: true
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

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
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

View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
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

View File

@@ -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

View File

@@ -19,11 +19,6 @@ module OrderManagement
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 = []
self.producer_ids = []
@@ -42,13 +37,6 @@ module OrderManagement
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.

View File

@@ -8,5 +8,20 @@ module Reports
@parameters = parameters
@permissions = permissions
end
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
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

View File

@@ -12,8 +12,22 @@ module Reports
end
end
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
# The parameters are never persisted.
def to_key; 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
end
end
end

View File

@@ -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")

View File

@@ -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")

View File

@@ -0,0 +1,2 @@
= render "filters"
= render "order_management/reports/report"

View File

@@ -0,0 +1 @@
= render "filters"

View File

@@ -1,2 +1,2 @@
= render "filters"
= render "report"
= render "order_management/reports/report"

View File

@@ -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

View File

@@ -0,0 +1,97 @@
# frozen_string_literal: true
require "spec_helper"
describe OrderManagement::Reports::BulkCoopController, type: :controller do
let(:report_klass) { OrderManagement::Reports::BulkCoop }
let!(:distributor) { create(:distributor_enterprise) }
let(:current_user) { distributor.owner }
before do
allow(controller).to receive(:spree_current_user) { current_user }
end
describe "#new" do
it "renders the report form" do
get :new
expect(response).to be_success
expect(response).to render_template(new_template_path)
end
end
describe "#create" do
context "when the parameters are valid" do
it "sends the generated report in the correct format" do
post :create, report: {
start_at: "2018-10-09 07:30:00",
report_type: "bulk_coop_supplier_report"
}, report_format: "csv"
expect(response).to be_success
expect(response.body).not_to be_blank
expect(response.header["Content-Type"]).to eq("text/csv")
end
end
context "when the parameters are invalid" do
it "renders the report form with an error" do
post :create, report: {
start_at: "invalid_date",
report_type: "bulk_coop_supplier_report"
}, report_format: "csv"
expect(flash[:error]).to eq(I18n.t("invalid_filter_parameters", scope: i18n_scope))
expect(response).to render_template(new_template_path)
end
end
context "when some parameters are now allowed" do
let!(:distributor) { create(:distributor_enterprise) }
let!(:other_distributor) { create(:distributor_enterprise) }
let(:current_user) { distributor.owner }
it "renders the report form with an error" do
post :create, report: {
distributor_ids: [other_distributor.id],
report_type: "bulk_coop_supplier_report"
}, report_format: "csv"
expect(flash[:error]).to eq(report_klass::Authorizer.parameter_not_allowed_error_message)
expect(response).to render_template(new_template_path)
end
end
describe "filtering results based on permissions" do
let!(:distributor) { create(:distributor_enterprise) }
let!(:other_distributor) { create(:distributor_enterprise) }
let(:current_user) { distributor.owner }
it "applies permissions to report" do
post :create, report: {}, report_format: "csv"
expect(assigns(:permissions).allowed_distributors.to_a).to eq([distributor])
end
end
end
private
def default_report_params
{
report_type: "bulk_coop_supplier_report"
}
end
def i18n_scope
"order_management.reports.enterprise_fee_summary"
end
def new_template_path
"order_management/reports/bulk_coop/new"
end
end

View File

@@ -0,0 +1,17 @@
# frozen_string_literal: true
require "spec_helper"
feature "bulk coop" do
include AuthenticationWorkflow
include WebHelper
scenario "bulk co-op report" do
quick_login_as_admin
visit spree.admin_reports_path
click_link 'Bulk Co-Op'
click_button 'Generate Report'
expect(page).to have_content 'Supplier'
end
end

View File

@@ -0,0 +1,81 @@
# frozen_string_literal: true
require 'spec_helper'
describe OrderManagement::Reports::BulkCoop::BulkCoopReport do
describe "fetching orders" do
let(:d1) { create(:distributor_enterprise) }
let(:oc1) { create(:simple_order_cycle) }
let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) }
let(:li1) { build(:line_item_with_shipment) }
before { o1.line_items << li1 }
context "as a site admin" do
let(:user) { create(:admin_user) }
subject { OrderManagement::Reports::BulkCoop::BulkCoopReport.new user, {}, true }
it "fetches completed orders" do
o2 = create(:order)
o2.line_items << build(:line_item)
expect(subject.table_items).to eq([li1])
end
it "does not show cancelled orders" do
o2 = create(:order, state: "canceled", completed_at: 1.day.ago)
o2.line_items << build(:line_item_with_shipment)
expect(subject.table_items).to eq([li1])
end
end
context "as a manager of a supplier" do
let!(:user) { create(:user) }
subject { OrderManagement::Reports::BulkCoop::BulkCoopReport.new user, {}, true }
let(:s1) { create(:supplier_enterprise) }
before do
s1.enterprise_roles.create!(user: user)
end
context "that has granted P-OC to the distributor" do
let(:o2) do
create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address),
ship_address: create(:address))
end
let(:li2) do
build(:line_item_with_shipment, product: create(:simple_product, supplier: s1))
end
before do
o2.line_items << li2
create(:enterprise_relationship, parent: s1, child: d1,
permissions_list: [:add_to_order_cycle])
end
it "shows line items supplied by my producers, with names hidden" do
expect(subject.table_items).to eq([li2])
expect(subject.table_items.first.order.bill_address.firstname).to eq("HIDDEN")
end
end
context "that has not granted P-OC to the distributor" do
let(:o2) do
create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address),
ship_address: create(:address))
end
let(:li2) do
build(:line_item_with_shipment, product: create(:simple_product, supplier: s1))
end
before do
o2.line_items << li2
end
it "does not show line items supplied by my producers" do
expect(subject.table_items).to eq([])
end
end
end
end
end

View File

@@ -1,140 +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"
[proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname },
proc { |lis| lis.first.product.name },
proc { |lis| lis.first.full_name },
proc { |lis| lis.sum(&:quantity) }]
when "bulk_coop_customer_payments"
[proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname },
proc { |lis| lis.first.order.completed_at.to_s },
proc { |lis| lis.map(&:order).uniq.sum(&:total) },
proc { |lis| lis.map(&:order).uniq.sum(&:outstanding_balance) },
proc { |lis| lis.map(&:order).uniq.sum(&:payment_total) }]
else
[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| lis.first.full_name },
proc { |lis| lis.first.weight_from_unit_value || 0 },
proc { |lis| lis.sum(&:quantity) },
proc { |lis| lis.sum { |li| li.max_quantity || 0 } },
proc { |_lis| "" },
proc { |_lis| "" }]
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
end
end

View File

@@ -1,8 +1,9 @@
module OpenFoodNetwork
class OrderGrouper
def initialize(rules, column_constructors)
def initialize(rules, column_constructors, report = nil)
@rules = rules
@column_constructors = column_constructors
@report = report
end
def build_tree(items, remaining_rules)
@@ -38,11 +39,11 @@ module OpenFoodNetwork
def build_table(groups)
rows = []
if is_leaf_node(groups)
rows << @column_constructors.map { |column_constructor| column_constructor.call(groups) }
rows << build_row(groups)
else
groups.each do |key, group|
if key == :summary_row
rows << group[:columns].map { |cols| cols.call(group[:items]) }
rows << build_summary_row(group[:columns], group[:items])
else
build_table(group).each { |g| rows << g }
end
@@ -59,6 +60,26 @@ module OpenFoodNetwork
private
def build_cell(column_constructor, items)
if column_constructor.is_a?(Symbol)
@report.__send__(column_constructor, items)
else
column_constructor.call(items)
end
end
def build_row(groups)
@column_constructors.map do |column_constructor|
build_cell(column_constructor, groups)
end
end
def build_summary_row(summary_row_column_constructors, items)
summary_row_column_constructors.map do |summary_row_column_constructor|
build_cell(summary_row_column_constructor, items)
end
end
def is_leaf_node(node)
node.is_a? Array
end

View File

@@ -1,64 +0,0 @@
require 'open_food_network/reports/bulk_coop_report'
module OpenFoodNetwork::Reports
class BulkCoopAllocationReport < BulkCoopReport
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
organise do
group(&:product)
sort(&:name)
summary_row do
column { |_lis| I18n.t('admin.reports.total') }
column { |lis| product_name(lis) }
column { |lis| group_buy_unit_size_f(lis) }
column { |_lis| "" }
column { |_lis| "" }
column { |_lis| "" }
column { |_lis| "" }
column { |lis| total_amount(lis) }
column { |lis| total_available(lis) }
column { |lis| remainder(lis) }
column { |lis| max_quantity_excess(lis) }
end
organise do
group(&:full_name)
sort { |full_name| full_name }
organise do
group(&:order)
sort(&:to_s)
end
end
end
columns do
column { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname }
column { |lis| lis.first.product.name }
column { |lis| lis.first.product.group_buy_unit_size || 0.0 }
column { |lis| lis.first.full_name }
column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first).value }
column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first).unit }
column { |lis| lis.first.weight_from_unit_value || 0 }
column { |lis| total_amount(lis) }
column { |_lis| "" }
column { |_lis| "" }
column { |_lis| "" }
end
end
end

View File

@@ -1,66 +0,0 @@
require 'open_food_network/reports/report'
module OpenFoodNetwork::Reports
class BulkCoopReport < Report
private
class << self
def supplier_name(lis)
lis.first.variant.product.supplier.name
end
def product_name(lis)
lis.first.variant.product.name
end
def group_buy_unit_size(lis)
(lis.first.variant.product.group_buy_unit_size || 0.0) /
(lis.first.product.variant_unit_scale || 1)
end
def group_buy_unit_size_f(lis)
group_buy_unit_size(lis)
end
def total_amount(lis)
lis.sum { |li| scaled_final_weight_volume(li) }
end
def units_required(lis)
if group_buy_unit_size(lis).zero?
0
else
( total_amount(lis) / group_buy_unit_size(lis) ).ceil
end
end
def total_available(lis)
units_required(lis) * group_buy_unit_size(lis)
end
def remainder(lis)
remainder = total_available(lis) - total_amount(lis)
remainder >= 0 ? remainder : ''
end
def max_quantity_excess(lis)
max_quantity_amount(lis) - total_amount(lis)
end
def max_quantity_amount(lis)
lis.sum do |li|
max_quantity = [li.max_quantity || 0, li.quantity || 0].max
max_quantity * scaled_unit_value(li.variant)
end
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
end
end
end

View File

@@ -1,64 +0,0 @@
require 'open_food_network/reports/bulk_coop_report'
module OpenFoodNetwork::Reports
class BulkCoopSupplierReport < BulkCoopReport
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
organise do
group { |li| li.product.supplier }
sort(&:name)
organise do
group(&:product)
sort(&:name)
summary_row do
column { |lis| supplier_name(lis) }
column { |lis| product_name(lis) }
column { |lis| group_buy_unit_size_f(lis) }
column { |_lis| "" }
column { |_lis| "" }
column { |_lis| "" }
column { |_lis| "" }
column { |lis| total_amount(lis) }
column { |lis| units_required(lis) }
column { |lis| remainder(lis) }
column { |lis| max_quantity_excess(lis) }
end
organise do
group(&:full_name)
sort { |full_name| full_name }
end
end
end
columns do
column { |lis| supplier_name(lis) }
column { |lis| product_name(lis) }
column { |lis| group_buy_unit_size_f(lis) }
column { |lis| lis.first.full_name }
column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first).value }
column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first).unit }
column { |lis| lis.first.weight_from_unit_value || 0 }
column { |lis| total_amount(lis) }
column { |_lis| '' }
column { |_lis| '' }
column { |_lis| '' }
end
end
end

View File

@@ -31,15 +31,5 @@ module OpenFoodNetwork::Reports
def self.header(*columns)
self._header = columns
end
def self.columns(&block)
self._columns = Row.new
Blockenspiel.invoke block, _columns
end
def self.organise(&block)
self._rules_head = Rule.new
Blockenspiel.invoke block, _rules_head
end
end
end

View File

@@ -1,7 +1,5 @@
module OpenFoodNetwork::Reports
class Row
include Blockenspiel::DSL
def initialize
@columns = []
end

View File

@@ -2,7 +2,6 @@ require 'open_food_network/reports/row'
module OpenFoodNetwork::Reports
class Rule
include Blockenspiel::DSL
attr_reader :next
def group(&block)
@@ -13,16 +12,6 @@ module OpenFoodNetwork::Reports
@sort = block
end
def summary_row(&block)
@summary_row = Row.new
Blockenspiel.invoke block, @summary_row
end
def organise(&block)
@next = Rule.new
Blockenspiel.invoke block, @next
end
def to_h
h = { group_by: @group, sort_by: @sort }
h[:summary_columns] = @summary_row.to_a if @summary_row

View File

@@ -93,18 +93,6 @@ describe Spree::Admin::ReportsController, type: :controller do
end
end
describe 'Bulk Coop' do
let!(:present_objects) { [orderA1, orderA2, orderB1, orderB2] }
it "only shows orders that I have access to" do
spree_post :bulk_coop, q: {}
expect(resulting_orders).to include(orderA1, orderB1)
expect(resulting_orders).not_to include(orderA2)
expect(resulting_orders).not_to include(orderB2)
end
end
describe 'Payments' do
let!(:present_objects) { [orderA1, orderA2, orderB1, orderB2] }
@@ -156,31 +144,6 @@ describe Spree::Admin::ReportsController, type: :controller do
end
end
describe 'Bulk Coop' do
context "where I have granted P-OC to the distributor" do
let!(:present_objects) { [orderA1, orderA2] }
before do
create(:enterprise_relationship, parent: supplier1, child: distributor1, permissions_list: [:add_to_order_cycle])
end
it "only shows product line items that I am supplying" do
spree_post :bulk_coop, q: {}
expect(resulting_products).to include product1
expect(resulting_products).not_to include product2, product3
end
end
context "where I have not granted P-OC to the distributor" do
it "shows product line items that I am supplying" do
spree_post :bulk_coop
expect(resulting_products).not_to include product1, product2, product3
end
end
end
describe 'Orders & Fulfillment' do
let!(:present_objects) { [orderA1, orderA2] }

View File

@@ -155,15 +155,6 @@ feature '
expect(page).to have_content 'Order date'
end
scenario "bulk co-op report" do
quick_login_as_admin
visit spree.admin_reports_path
click_link 'Bulk Co-Op'
click_button 'Search'
expect(page).to have_content 'Supplier'
end
scenario "payments reports" do
quick_login_as_admin
visit spree.admin_reports_path

View File

@@ -1,98 +0,0 @@
require 'spec_helper'
require 'open_food_network/bulk_coop_report'
include AuthenticationWorkflow
module OpenFoodNetwork
describe BulkCoopReport do
describe "fetching orders" do
let(:d1) { create(:distributor_enterprise) }
let(:oc1) { create(:simple_order_cycle) }
let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) }
let(:li1) { build(:line_item_with_shipment) }
before { o1.line_items << li1 }
context "as a site admin" do
let(:user) { create(:admin_user) }
subject { BulkCoopReport.new user, {}, true }
it "fetches completed orders" do
o2 = create(:order)
o2.line_items << build(:line_item)
expect(subject.table_items).to eq([li1])
end
it "does not show cancelled orders" do
o2 = create(:order, state: "canceled", completed_at: 1.day.ago)
o2.line_items << build(:line_item_with_shipment)
expect(subject.table_items).to eq([li1])
end
end
context "as a manager of a supplier" do
let!(:user) { create(:user) }
subject { BulkCoopReport.new user, {}, true }
let(:s1) { create(:supplier_enterprise) }
before do
s1.enterprise_roles.create!(user: user)
end
context "that has granted P-OC to the distributor" do
let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) }
let(:li2) { build(:line_item_with_shipment, product: create(:simple_product, supplier: s1)) }
before do
o2.line_items << li2
create(:enterprise_relationship, parent: s1, child: d1, permissions_list: [:add_to_order_cycle])
end
it "shows line items supplied by my producers, with names hidden" do
expect(subject.table_items).to eq([li2])
expect(subject.table_items.first.order.bill_address.firstname).to eq("HIDDEN")
end
end
context "that has not granted P-OC to the distributor" do
let(:o2) { create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address), ship_address: create(:address)) }
let(:li2) { build(:line_item_with_shipment, product: create(:simple_product, supplier: s1)) }
before do
o2.line_items << li2
end
it "does not show line items supplied by my producers" do
expect(subject.table_items).to eq([])
end
end
end
context "as a manager of a distributor" do
let!(:user) { create(:user) }
subject { PackingReport.new user, {}, true }
before do
d1.enterprise_roles.create!(user: user)
end
it "only shows line items distributed by enterprises managed by the current user" do
d2 = create(:distributor_enterprise)
d2.enterprise_roles.create!(user: create(:user))
o2 = create(:order, distributor: d2, completed_at: 1.day.ago)
o2.line_items << build(:line_item_with_shipment)
expect(subject.table_items).to eq([li1])
end
it "only shows the selected order cycle" do
oc2 = create(:simple_order_cycle)
o2 = create(:order, distributor: d1, order_cycle: oc2)
o2.line_items << build(:line_item)
allow(subject).to receive(:params).and_return(order_cycle_id_in: oc1.id)
expect(subject.table_items).to eq([li1])
end
end
end
end
end

View File

@@ -1,98 +1,15 @@
require 'open_food_network/reports/report'
module OpenFoodNetwork::Reports
P1 = proc { |o| o[:one] }
P2 = proc { |o| o[:two] }
P3 = proc { |o| o[:three] }
P4 = proc { |o| o[:four] }
class TestReport < Report
header 'One', 'Two', 'Three', 'Four'
columns do
column(&P1)
column(&P2)
column(&P3)
column(&P4)
end
organise do
group(&P1)
sort(&P2)
organise do
group(&P3)
sort(&P4)
summary_row do
column(&P1)
column(&P4)
end
end
end
end
class HelperReport < Report
columns do
column { |obj| my_helper(obj) }
end
private
def self.my_helper(obj)
obj[:one]
end
end
describe Report do
let(:report) { TestReport.new }
let(:helper_report) { HelperReport.new }
let(:rules_head) { TestReport._rules_head }
let(:data) { { one: 1, two: 2, three: 3, four: 4 } }
it "returns the header" do
expect(report.header).to eq(%w(One Two Three Four))
end
it "returns columns as an array of procs" do
expect(report.columns[0].call(data)).to eq(1)
expect(report.columns[1].call(data)).to eq(2)
expect(report.columns[2].call(data)).to eq(3)
expect(report.columns[3].call(data)).to eq(4)
end
it "supports helpers when outputting columns" do
expect(helper_report.columns[0].call(data)).to eq(1)
end
describe "rules" do
let(:group_by) { rules_head.to_h[:group_by] }
let(:sort_by) { rules_head.to_h[:sort_by] }
let(:next_group_by) { rules_head.next.to_h[:group_by] }
let(:next_sort_by) { rules_head.next.to_h[:sort_by] }
let(:next_summary_columns) { rules_head.next.to_h[:summary_columns] }
it "constructs the head of the rules list" do
expect(group_by.call(data)).to eq(1)
expect(sort_by.call(data)).to eq(2)
end
it "constructs nested rules" do
expect(next_group_by.call(data)).to eq(3)
expect(next_sort_by.call(data)).to eq(4)
end
it "constructs summary columns for rules" do
expect(next_summary_columns[0].call(data)).to eq(1)
expect(next_summary_columns[1].call(data)).to eq(4)
end
end
describe "outputting rules" do
it "outputs the rules" do
expect(report.rules).to eq([{ group_by: P1, sort_by: P2 },
{ group_by: P3, sort_by: P4, summary_columns: [P1, P4] }])
end
end
end
end

View File

@@ -17,23 +17,5 @@ module OpenFoodNetwork::Reports
rule.sort(&proc)
expect(rule.to_h).to eq(group_by: nil, sort_by: proc)
end
it "can define a nested rule" do
rule.organise(&proc)
expect(rule.next).to be_a Rule
end
it "can define a summary row and return it in a hash" do
rule.summary_row do
column {}
column {}
column {}
end
expect(rule.to_h[:summary_columns].count).to eq(3)
expect(rule.to_h[:summary_columns][0]).to be_a Proc
expect(rule.to_h[:summary_columns][1]).to be_a Proc
expect(rule.to_h[:summary_columns][2]).to be_a Proc
end
end
end