mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Report Refactor 3: BulkCoop
This commit is contained in:
committed by
Jean-Baptiste Bellet
parent
22fe652e89
commit
944d40e093
@@ -9,11 +9,9 @@ module Reporting
|
||||
delegate :formatted_rules, :header_option?, :summary_row_option?, to: :ruler
|
||||
delegate :grouped_data, :rows, to: :grouper
|
||||
|
||||
OPTIONAL_HEADERS = [].freeze
|
||||
|
||||
def initialize(user, params)
|
||||
def initialize(user, params = {})
|
||||
@user = user
|
||||
@params = params || {}
|
||||
@params = params
|
||||
@params = @params.permit!.to_h unless @params.is_a? Hash
|
||||
@ransack_params = @params[:q] || {}
|
||||
end
|
||||
|
||||
47
lib/reporting/reports/bulk_coop/allocation.rb
Normal file
47
lib/reporting/reports/bulk_coop/allocation.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class Allocation < Base
|
||||
def query_result
|
||||
table_items.group_by(&:order).values
|
||||
end
|
||||
|
||||
def columns
|
||||
{
|
||||
customer: :order_billing_address_name,
|
||||
product: :product_name,
|
||||
bulk_unit_size: :product_group_buy_unit_size,
|
||||
variant: :full_name,
|
||||
variant_value: :option_value_value,
|
||||
variant_unit: :option_value_unit,
|
||||
weight: :weight_from_unit_value,
|
||||
sum_total: :total_amount,
|
||||
total_available: :empty_cell,
|
||||
unallocated: :empty_cell,
|
||||
max_quantity_excess: :empty_cell
|
||||
}
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: :product,
|
||||
header: true,
|
||||
summary_row: proc do |_key, items, rows|
|
||||
line_items = items.flatten
|
||||
{
|
||||
sum_total: rows.sum(&:sum_total),
|
||||
total_available: total_available(line_items),
|
||||
unallocated: remainder(line_items),
|
||||
max_quantity_excess: max_quantity_excess(line_items)
|
||||
}
|
||||
end
|
||||
}
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
131
lib/reporting/reports/bulk_coop/base.rb
Normal file
131
lib/reporting/reports/bulk_coop/base.rb
Normal file
@@ -0,0 +1,131 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class Base < ReportObjectTemplate
|
||||
def message
|
||||
I18n.t("spree.admin.reports.customer_names_message.customer_names_tip")
|
||||
end
|
||||
|
||||
def search
|
||||
report_line_items.orders
|
||||
end
|
||||
|
||||
def table_items
|
||||
report_line_items.list(line_item_includes)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def line_item_includes
|
||||
[
|
||||
{
|
||||
order: [:bill_address],
|
||||
variant: [{ option_values: :option_type }, { product: :supplier }]
|
||||
},
|
||||
:option_values
|
||||
]
|
||||
end
|
||||
|
||||
def order_permissions
|
||||
@order_permissions ||= ::Permissions::Order.new(@user)
|
||||
end
|
||||
|
||||
def report_line_items
|
||||
@report_line_items ||= Reporting::LineItems.new(
|
||||
order_permissions,
|
||||
@params,
|
||||
CompleteVisibleOrders.new(order_permissions).query
|
||||
)
|
||||
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)
|
||||
unit_size = line_items.first.variant.product.group_buy_unit_size || 0.0
|
||||
unit_size / (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 scaled_unit_value(variant)
|
||||
(variant.unit_value || 0) / (variant.product.variant_unit_scale || 1)
|
||||
end
|
||||
|
||||
def option_value_value(line_items)
|
||||
VariantUnits::OptionValueNamer.new(line_items.first).value
|
||||
end
|
||||
|
||||
def option_value_unit(line_items)
|
||||
VariantUnits::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 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 remainder(line_items)
|
||||
remainder = total_available(line_items) - total_amount(line_items)
|
||||
remainder >= 0 ? remainder : ''
|
||||
end
|
||||
|
||||
def total_amount(line_items)
|
||||
line_items.sum { |li| scaled_final_weight_volume(li) }
|
||||
end
|
||||
|
||||
def scaled_final_weight_volume(line_item)
|
||||
(line_item.final_weight_volume || 0) / (line_item.product.variant_unit_scale || 1)
|
||||
end
|
||||
|
||||
def total_available(line_items)
|
||||
units_required(line_items) * group_buy_unit_size(line_items)
|
||||
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 weight_from_unit_value(line_items)
|
||||
line_items.first.weight_from_unit_value || 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,67 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class BulkCoopAllocationReport
|
||||
def table_headers
|
||||
[
|
||||
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
|
||||
@@ -1,317 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class BulkCoopReport < ReportObjectTemplate
|
||||
def initialize(user, params = {})
|
||||
super(user, params)
|
||||
|
||||
@supplier_report = BulkCoopSupplierReport.new
|
||||
@allocation_report = BulkCoopAllocationReport.new
|
||||
@filter_canceled = false
|
||||
end
|
||||
|
||||
def message
|
||||
I18n.t("spree.admin.reports.customer_names_message.customer_names_tip")
|
||||
end
|
||||
|
||||
def table_headers
|
||||
case params[:report_subtype]
|
||||
when "bulk_coop_supplier_report"
|
||||
@supplier_report.table_headers
|
||||
when "bulk_coop_allocation"
|
||||
@allocation_report.table_headers
|
||||
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
|
||||
report_line_items.list(line_item_includes)
|
||||
end
|
||||
|
||||
def table_rows
|
||||
order_grouper = Reporting::OrderGrouper.new rules, columns, self
|
||||
order_grouper.table(table_items)
|
||||
end
|
||||
|
||||
def rules
|
||||
case params[:report_subtype]
|
||||
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|
|
||||
( if (lis.first.product.group_buy_unit_size || 0).zero?
|
||||
0
|
||||
else
|
||||
( lis.sum { |li|
|
||||
[li.max_quantity || 0,
|
||||
li.quantity || 0].max * (li.weight_from_unit_value || 0)
|
||||
} / lis.first.product.group_buy_unit_size )
|
||||
end ).floor
|
||||
},
|
||||
proc { |lis|
|
||||
lis.sum { |li|
|
||||
[li.max_quantity || 0,
|
||||
li.quantity || 0].max * (li.weight_from_unit_value || 0)
|
||||
} - ( ( if (lis.first.product.group_buy_unit_size || 0).zero?
|
||||
0
|
||||
else
|
||||
( lis.sum { |li|
|
||||
[li.max_quantity || 0,
|
||||
li.quantity || 0].max * (li.weight_from_unit_value || 0)
|
||||
} / lis.first.product.group_buy_unit_size )
|
||||
end ).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_subtype]
|
||||
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
|
||||
|
||||
attr_reader :filter_canceled
|
||||
|
||||
def line_item_includes
|
||||
[
|
||||
{
|
||||
order: [:bill_address],
|
||||
variant: [{ option_values: :option_type }, { product: :supplier }]
|
||||
},
|
||||
:option_values
|
||||
]
|
||||
end
|
||||
|
||||
def order_permissions
|
||||
@order_permissions ||= ::Permissions::Order.new(@user, filter_canceled)
|
||||
end
|
||||
|
||||
def report_line_items
|
||||
@report_line_items ||= Reporting::LineItems.new(
|
||||
order_permissions,
|
||||
@params,
|
||||
CompleteVisibleOrders.new(order_permissions).query
|
||||
)
|
||||
end
|
||||
|
||||
def customer_payments_total_cost(line_items)
|
||||
unique_orders(line_items).sum(&:total)
|
||||
end
|
||||
|
||||
def customer_payments_amount_owed(line_items)
|
||||
unique_orders(line_items).sum(&:new_outstanding_balance)
|
||||
end
|
||||
|
||||
def customer_payments_amount_paid(line_items)
|
||||
unique_orders(line_items).sum(&:payment_total)
|
||||
end
|
||||
|
||||
def unique_orders(line_items)
|
||||
line_items.map(&:order).uniq
|
||||
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)
|
||||
unit_size = line_items.first.variant.product.group_buy_unit_size || 0.0
|
||||
unit_size / (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)
|
||||
VariantUnits::OptionValueNamer.new(line_items.first).value
|
||||
end
|
||||
|
||||
def option_value_unit(line_items)
|
||||
VariantUnits::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
|
||||
@@ -1,65 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class BulkCoopSupplierReport
|
||||
def table_headers
|
||||
[
|
||||
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
|
||||
61
lib/reporting/reports/bulk_coop/customer_payments.rb
Normal file
61
lib/reporting/reports/bulk_coop/customer_payments.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class CustomerPayments < Base
|
||||
def query_result
|
||||
table_items.group_by(&:order).values
|
||||
end
|
||||
|
||||
def columns
|
||||
{
|
||||
customer: :order_billing_address_name,
|
||||
date_of_order: :order_completed_at,
|
||||
total_cost: :customer_payments_total_cost,
|
||||
amount_owing: :customer_payments_amount_owed,
|
||||
amount_paid: :customer_payments_amount_paid
|
||||
}
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: :customer,
|
||||
header: true,
|
||||
summary_row: proc do |_key, _items, rows|
|
||||
{
|
||||
total_cost: rows.sum(&:total_cost),
|
||||
amount_owing: rows.sum(&:amount_owing),
|
||||
amount_paid: rows.sum(&:amount_paid),
|
||||
}
|
||||
end
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def customer_payments_total_cost(line_items)
|
||||
unique_orders(line_items).sum(&:total)
|
||||
end
|
||||
|
||||
def customer_payments_amount_owed(line_items)
|
||||
unique_orders(line_items).sum(&:new_outstanding_balance)
|
||||
end
|
||||
|
||||
def customer_payments_amount_paid(line_items)
|
||||
unique_orders(line_items).sum(&:payment_total)
|
||||
end
|
||||
|
||||
def unique_orders(line_items)
|
||||
line_items.map(&:order).uniq
|
||||
end
|
||||
|
||||
def order_completed_at(line_items)
|
||||
line_items.first.order.completed_at
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
28
lib/reporting/reports/bulk_coop/packing_sheets.rb
Normal file
28
lib/reporting/reports/bulk_coop/packing_sheets.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class PackingSheets < Base
|
||||
def query_result
|
||||
table_items.group_by(&:order).values
|
||||
end
|
||||
|
||||
def columns
|
||||
{
|
||||
customer: :order_billing_address_name,
|
||||
product: :product_name,
|
||||
variant: :full_name,
|
||||
sum_total: :total_quantity
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def total_quantity(line_items)
|
||||
line_items.sum(&:quantity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
53
lib/reporting/reports/bulk_coop/supplier_report.rb
Normal file
53
lib/reporting/reports/bulk_coop/supplier_report.rb
Normal file
@@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class SupplierReport < Base
|
||||
def query_result
|
||||
table_items.group_by(&:variant).values
|
||||
end
|
||||
|
||||
def columns
|
||||
{
|
||||
supplier: :variant_product_supplier_name,
|
||||
product: :variant_product_name,
|
||||
bulk_unit_size: :variant_product_group_buy_unit_size_f,
|
||||
variant: :full_name,
|
||||
variant_value: :option_value_value,
|
||||
variant_unit: :option_value_unit,
|
||||
weight: :weight_from_unit_value,
|
||||
sum_total: :total_amount,
|
||||
units_required: :empty_cell,
|
||||
unallocated: :empty_cell,
|
||||
max_quantity_excess: :empty_cell
|
||||
}
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: :supplier,
|
||||
header: true,
|
||||
summary_row: proc do |_key, items, rows|
|
||||
line_items = items.flatten
|
||||
{
|
||||
sum_total: rows.sum(&:sum_total),
|
||||
units_required: units_required(line_items),
|
||||
unallocated: remainder(line_items),
|
||||
max_quantity_excess: max_quantity_excess(line_items)
|
||||
}
|
||||
end
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def variant_product_supplier_name(line_items)
|
||||
line_items.first.variant.product.supplier.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -89,15 +89,15 @@ module Reporting
|
||||
|
||||
def bulk_coop_report_types
|
||||
[
|
||||
bulk_coop_item(:bulk_coop_supplier_report),
|
||||
bulk_coop_item(:bulk_coop_allocation),
|
||||
bulk_coop_item(:bulk_coop_packing_sheets),
|
||||
bulk_coop_item(:bulk_coop_customer_payments)
|
||||
bulk_coop_item(:supplier_report),
|
||||
bulk_coop_item(:allocation),
|
||||
bulk_coop_item(:packing_sheets),
|
||||
bulk_coop_item(:customer_payments)
|
||||
]
|
||||
end
|
||||
|
||||
def bulk_coop_item(key)
|
||||
[I18n.t("order_management.reports.bulk_coop.filters.#{key}"), key]
|
||||
[I18n.t("order_management.reports.bulk_coop.filters.bulk_coop_#{key}"), key]
|
||||
end
|
||||
|
||||
def i18n_translate(key)
|
||||
|
||||
@@ -2,185 +2,196 @@
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Reporting::Reports::BulkCoop::BulkCoopReport do
|
||||
subject { Reporting::Reports::BulkCoop::BulkCoopReport.new user, params }
|
||||
let(:user) { create(:admin_user) }
|
||||
# rubocop:disable Metrics/ModuleLength
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
describe Base do
|
||||
subject { Base.new user, params }
|
||||
let(:user) { create(:admin_user) }
|
||||
|
||||
describe '#table_items' do
|
||||
let(:params) { {} }
|
||||
describe '#query_result' do
|
||||
let(:params) { {} }
|
||||
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) }
|
||||
|
||||
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 }
|
||||
|
||||
before { o1.line_items << li1 }
|
||||
context "as a site admin" do
|
||||
context 'when searching' do
|
||||
let(:params) {
|
||||
{ q: { completed_at_gt: '', completed_at_lt: '', distributor_id_in: [] } }
|
||||
}
|
||||
|
||||
context "as a site admin" do
|
||||
context 'when searching' do
|
||||
let(:params) { { q: { completed_at_gt: '', completed_at_lt: '', distributor_id_in: [] } } }
|
||||
it "fetches completed orders" do
|
||||
o2 = create(:order, state: 'cart')
|
||||
o2.line_items << build(:line_item)
|
||||
expect(subject.table_items).to eq([li1])
|
||||
end
|
||||
|
||||
it "fetches completed orders" do
|
||||
o2 = create(:order, state: 'cart')
|
||||
o2.line_items << build(:line_item)
|
||||
expect(subject.table_items).to eq([li1])
|
||||
it 'shows canceled orders' do
|
||||
o2 = create(:order, state: 'canceled', completed_at: 1.day.ago, order_cycle: oc1,
|
||||
distributor: d1)
|
||||
line_item = build(:line_item_with_shipment)
|
||||
o2.line_items << line_item
|
||||
expect(subject.table_items).to include(line_item)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not searching' do
|
||||
let(:params) { {} }
|
||||
|
||||
it "fetches completed orders" do
|
||||
o2 = create(:order, state: 'cart')
|
||||
o2.line_items << build(:line_item)
|
||||
expect(subject.table_items).to eq([li1])
|
||||
end
|
||||
|
||||
it 'shows canceled orders' do
|
||||
o2 = create(:order, state: 'canceled', completed_at: 1.day.ago, order_cycle: oc1,
|
||||
distributor: d1)
|
||||
line_item = build(:line_item_with_shipment)
|
||||
o2.line_items << line_item
|
||||
expect(subject.table_items).to include(line_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "filtering by date" do
|
||||
it do
|
||||
user = create(:admin_user)
|
||||
o2 = create(:order, completed_at: 3.days.ago, order_cycle: oc1, distributor: d1)
|
||||
li2 = build(:line_item_with_shipment)
|
||||
o2.line_items << li2
|
||||
|
||||
report = Base.new user, {}
|
||||
expect(report.table_items).to match_array [li1, li2]
|
||||
|
||||
report = Base.new(
|
||||
user, { q: { completed_at_gt: 2.days.ago } }
|
||||
)
|
||||
expect(report.table_items).to eq([li1])
|
||||
|
||||
report = Base.new(
|
||||
user, { q: { completed_at_lt: 2.days.ago } }
|
||||
)
|
||||
expect(report.table_items).to eq([li2])
|
||||
end
|
||||
end
|
||||
|
||||
context "filtering by distributor" do
|
||||
it do
|
||||
user = create(:admin_user)
|
||||
d2 = create(:distributor_enterprise)
|
||||
o2 = create(:order, distributor: d2, order_cycle: oc1,
|
||||
completed_at: Time.zone.now)
|
||||
li2 = build(:line_item_with_shipment)
|
||||
o2.line_items << li2
|
||||
|
||||
report = Base.new user, {}
|
||||
expect(report.table_items).to match_array [li1, li2]
|
||||
|
||||
report = Base.new(
|
||||
user, { q: { distributor_id_in: [d1.id] } }
|
||||
)
|
||||
expect(report.table_items).to eq([li1])
|
||||
|
||||
report = Base.new(
|
||||
user, { q: { distributor_id_in: [d2.id] } }
|
||||
)
|
||||
expect(report.table_items).to eq([li2])
|
||||
end
|
||||
end
|
||||
|
||||
context "as a manager of a supplier" do
|
||||
let!(:user) { create(:user) }
|
||||
subject { Base.new user, {} }
|
||||
|
||||
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
|
||||
|
||||
it 'shows canceled orders' do
|
||||
o2 = create(:order, state: 'canceled', completed_at: 1.day.ago, order_cycle: oc1,
|
||||
distributor: d1)
|
||||
line_item = build(:line_item_with_shipment)
|
||||
o2.line_items << line_item
|
||||
expect(subject.table_items).to include(line_item)
|
||||
describe '#columns' do
|
||||
context 'when report type is bulk_coop_customer_payments' do
|
||||
subject { CustomerPayments.new user }
|
||||
|
||||
it 'returns' do
|
||||
expect(subject.columns.values).to match_array(
|
||||
[
|
||||
:order_billing_address_name,
|
||||
:order_completed_at,
|
||||
:customer_payments_total_cost,
|
||||
:customer_payments_amount_owed,
|
||||
:customer_payments_amount_paid,
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Yes, I know testing a private method is bad practice but report's design, tighly coupling
|
||||
# Reporting::OrderGrouper and Base, makes it
|
||||
# very hard to make things testeable without ending up in a wormwhole. This is a trade-off.
|
||||
describe '#customer_payments_amount_owed' do
|
||||
let(:params) { {} }
|
||||
let(:user) { build(:user) }
|
||||
let!(:line_item) { create(:line_item) }
|
||||
let(:order) { line_item.order }
|
||||
|
||||
it 'calls #new_outstanding_balance' do
|
||||
expect_any_instance_of(Spree::Order).to receive(:new_outstanding_balance)
|
||||
CustomerPayments.new(user).__send__(:customer_payments_amount_owed, [line_item])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not searching' do
|
||||
let(:params) { {} }
|
||||
|
||||
it "fetches completed orders" do
|
||||
o2 = create(:order, state: 'cart')
|
||||
o2.line_items << build(:line_item)
|
||||
expect(subject.table_items).to eq([li1])
|
||||
end
|
||||
|
||||
it 'shows canceled orders' do
|
||||
o2 = create(:order, state: 'canceled', completed_at: 1.day.ago, order_cycle: oc1,
|
||||
distributor: d1)
|
||||
line_item = build(:line_item_with_shipment)
|
||||
o2.line_items << line_item
|
||||
expect(subject.table_items).to include(line_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "filtering by date" do
|
||||
it do
|
||||
user = create(:admin_user)
|
||||
o2 = create(:order, completed_at: 3.days.ago, order_cycle: oc1, distributor: d1)
|
||||
li2 = build(:line_item_with_shipment)
|
||||
o2.line_items << li2
|
||||
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new user, {}
|
||||
expect(report.table_items).to match_array [li1, li2]
|
||||
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new(
|
||||
user, { q: { completed_at_gt: 2.days.ago } }
|
||||
)
|
||||
expect(report.table_items).to eq([li1])
|
||||
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new(
|
||||
user, { q: { completed_at_lt: 2.days.ago } }
|
||||
)
|
||||
expect(report.table_items).to eq([li2])
|
||||
end
|
||||
end
|
||||
|
||||
context "filtering by distributor" do
|
||||
it do
|
||||
user = create(:admin_user)
|
||||
d2 = create(:distributor_enterprise)
|
||||
o2 = create(:order, distributor: d2, order_cycle: oc1,
|
||||
completed_at: Time.zone.now)
|
||||
li2 = build(:line_item_with_shipment)
|
||||
o2.line_items << li2
|
||||
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new user, {}
|
||||
expect(report.table_items).to match_array [li1, li2]
|
||||
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new(
|
||||
user, { q: { distributor_id_in: [d1.id] } }
|
||||
)
|
||||
expect(report.table_items).to eq([li1])
|
||||
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new(
|
||||
user, { q: { distributor_id_in: [d2.id] } }
|
||||
)
|
||||
expect(report.table_items).to eq([li2])
|
||||
end
|
||||
end
|
||||
|
||||
context "as a manager of a supplier" do
|
||||
let!(:user) { create(:user) }
|
||||
subject { Reporting::Reports::BulkCoop::BulkCoopReport.new user, {} }
|
||||
|
||||
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
|
||||
|
||||
describe '#columns' do
|
||||
context 'when report type is bulk_coop_customer_payments' do
|
||||
let(:params) { { report_subtype: 'bulk_coop_customer_payments' } }
|
||||
|
||||
it 'returns' do
|
||||
expect(subject.columns).to eq(
|
||||
[
|
||||
:order_billing_address_name,
|
||||
:order_completed_at,
|
||||
:customer_payments_total_cost,
|
||||
:customer_payments_amount_owed,
|
||||
:customer_payments_amount_paid,
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Yes, I know testing a private method is bad practice but report's design, tighly coupling
|
||||
# Reporting::OrderGrouper and Reporting::Reports::BulkCoop::BulkCoopReport, makes it
|
||||
# very hard to make things testeable without ending up in a wormwhole. This is a trade-off.
|
||||
describe '#customer_payments_amount_owed' do
|
||||
let(:params) { {} }
|
||||
let(:user) { build(:user) }
|
||||
let!(:line_item) { create(:line_item) }
|
||||
let(:order) { line_item.order }
|
||||
|
||||
it 'calls #new_outstanding_balance' do
|
||||
expect_any_instance_of(Spree::Order).to receive(:new_outstanding_balance)
|
||||
subject.__send__(:customer_payments_amount_owed, [line_item])
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ModuleLength
|
||||
|
||||
@@ -390,7 +390,7 @@ describe '
|
||||
click_link 'Bulk Co-Op'
|
||||
end
|
||||
|
||||
xit "generating Bulk Co-op Supplier Report" do
|
||||
it "generating Bulk Co-op Supplier Report" do
|
||||
select "Bulk Co-op Supplier Report", from: "report_subtype"
|
||||
click_button 'Go'
|
||||
|
||||
@@ -406,10 +406,10 @@ describe '
|
||||
"Units Required",
|
||||
"Unallocated",
|
||||
"Max Quantity Excess"
|
||||
]
|
||||
].map(&:upcase)
|
||||
end
|
||||
|
||||
xit "generating Bulk Co-op Allocation report" do
|
||||
it "generating Bulk Co-op Allocation report" do
|
||||
select "Bulk Co-op Allocation", from: "report_subtype"
|
||||
click_button 'Go'
|
||||
|
||||
@@ -425,10 +425,10 @@ describe '
|
||||
"Total available",
|
||||
"Unallocated",
|
||||
"Max Quantity Excess"
|
||||
]
|
||||
].map(&:upcase)
|
||||
end
|
||||
|
||||
xit "generating Bulk Co-op Packing Sheets report" do
|
||||
it "generating Bulk Co-op Packing Sheets report" do
|
||||
select "Bulk Co-op Packing Sheets", from: "report_subtype"
|
||||
click_button 'Go'
|
||||
|
||||
@@ -437,10 +437,10 @@ describe '
|
||||
"Product",
|
||||
"Variant",
|
||||
"Sum Total"
|
||||
]
|
||||
].map(&:upcase)
|
||||
end
|
||||
|
||||
xit "generating Bulk Co-op Customer Payments report" do
|
||||
it "generating Bulk Co-op Customer Payments report" do
|
||||
select "Bulk Co-op Customer Payments", from: "report_subtype"
|
||||
click_button 'Go'
|
||||
|
||||
@@ -450,7 +450,7 @@ describe '
|
||||
"Total Cost",
|
||||
"Amount Owing",
|
||||
"Amount Paid"
|
||||
]
|
||||
].map(&:upcase)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user