mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Auto-merge from CI [skip ci]
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -50,6 +50,7 @@ gem 'custom_error_message', :github => 'jeremydurham/custom-err-msg'
|
||||
gem 'angularjs-file-upload-rails', '~> 1.1.0'
|
||||
gem 'roadie-rails', '~> 1.0.3'
|
||||
gem 'figaro'
|
||||
gem 'blockenspiel'
|
||||
gem 'acts-as-taggable-on', '~> 3.4'
|
||||
|
||||
gem 'foreigner'
|
||||
|
||||
@@ -165,6 +165,7 @@ GEM
|
||||
bcrypt (3.1.7)
|
||||
bcrypt-ruby (3.1.5)
|
||||
bcrypt (>= 3.1.3)
|
||||
blockenspiel (0.4.5)
|
||||
bugsnag (1.5.2)
|
||||
httparty (>= 0.6, < 1.0)
|
||||
multi_json (~> 1.0)
|
||||
@@ -546,6 +547,7 @@ DEPENDENCIES
|
||||
angularjs-rails (= 1.2.13)
|
||||
awesome_print
|
||||
aws-sdk
|
||||
blockenspiel
|
||||
bugsnag
|
||||
capybara
|
||||
coffee-rails (~> 3.2.1)
|
||||
|
||||
@@ -15,7 +15,7 @@ We're part of global movement - get involved!
|
||||
## Dependencies
|
||||
|
||||
* Rails 3.2.x
|
||||
* Ruby >= 1.9.3
|
||||
* Ruby 1.9.3
|
||||
* PostgreSQL database
|
||||
* PhantomJS (for testing)
|
||||
* See Gemfile for a list of gems required
|
||||
@@ -32,19 +32,20 @@ You can view the code at:
|
||||
|
||||
You can download the source with the command:
|
||||
|
||||
git clone git@github.com:openfoodfoundation/openfoodnetwork
|
||||
git clone https://github.com/openfoodfoundation/openfoodnetwork.git
|
||||
|
||||
|
||||
## Get it running
|
||||
|
||||
For those new to Rails, the following tutorial will help get you up to speed with configuring a Rails environment: http://guides.rubyonrails.org/getting_started.html .
|
||||
|
||||
First, check your dependencies: Ensure that you have Ruby 1.9.x installed:
|
||||
First, check your dependencies: Ensure that you have Ruby >= 1.9.3 installed:
|
||||
|
||||
ruby --version
|
||||
|
||||
Install the project's gem dependencies:
|
||||
|
||||
cd openfoodnetwork
|
||||
bundle install
|
||||
|
||||
Configure the site:
|
||||
|
||||
@@ -9,8 +9,8 @@ Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter) ->
|
||||
# Adding methods to each enterprise
|
||||
extend: (enterprise) ->
|
||||
new class MapMarker
|
||||
# We're whitelisting attributes because GMaps tries to crawl
|
||||
# our data, and our data is recursive, so it breaks
|
||||
# We cherry-pick attributes because GMaps tries to crawl
|
||||
# our data, and our data is cyclic, so it breaks
|
||||
latitude: enterprise.latitude
|
||||
longitude: enterprise.longitude
|
||||
icon: enterprise.icon
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
%ng-include{src: "'partials/enterprise_header.html'"}
|
||||
%ng-include{src: "'partials/enterprise_details.html'"}
|
||||
%ng-include{src: "'partials/hub_details.html'"}
|
||||
%ng-include{src: "'partials/producer_details.html'"}
|
||||
%ng-include{src: "'partials/close.html'"}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
-# Show places to buy products from this producer, when there are any
|
||||
-# Do not show this for producer shops selling only their own produce,
|
||||
-# Since a shopping link will already have been displayed in hub_details.html.haml
|
||||
.row.active_table_row.pad-top{bindonce: true, "ng-if" => "enterprise.is_primary_producer && enterprise.hubs.length > 0 && !(enterprise.hubs.length == 1 && enterprise.hubs[0] == enterprise)"}
|
||||
.columns.small-12
|
||||
.row
|
||||
.columns.small-12.fat
|
||||
%div{"bo-if" => "enterprise.name"}
|
||||
%label
|
||||
Shop for
|
||||
%span.turquoise{"bo-text" => "enterprise.name"}
|
||||
products at:
|
||||
%div.show-for-medium-up{"bo-if" => "!enterprise.name"}
|
||||
|
||||
.row.cta-container
|
||||
.columns.small-12
|
||||
%a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | filter:{id: '!'+enterprise.id} | orderBy:'-active'",
|
||||
"bo-href" => "hub.path", "ofn-empties-cart" => "hub",
|
||||
"bo-class" => "{primary: hub.active, secondary: !hub.active}"}
|
||||
%i.ofn-i_033-open-sign{"bo-if" => "hub.active"}
|
||||
%i.ofn-i_032-closed-sign{"bo-if" => "!hub.active"}
|
||||
.hub-name{"bo-text" => "hub.name"}
|
||||
.button-address{"bo-bind" => "[hub.address.city, hub.address.state_name] | printArray"}
|
||||
@@ -9,6 +9,9 @@ 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'
|
||||
|
||||
Spree::Admin::ReportsController.class_eval do
|
||||
|
||||
@@ -83,17 +86,8 @@ Spree::Admin::ReportsController.class_eval do
|
||||
end
|
||||
|
||||
def packing
|
||||
# -- Prepare parameters
|
||||
params[:q] ||= {}
|
||||
if params[:q][:completed_at_gt].blank?
|
||||
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
|
||||
else
|
||||
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]) rescue Time.zone.now.beginning_of_month
|
||||
end
|
||||
if params[:q] && !params[:q][:completed_at_lt].blank?
|
||||
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]) rescue ""
|
||||
end
|
||||
params[:q][:meta_sort] ||= "completed_at.desc"
|
||||
# -- Prepare date parameters
|
||||
prepare_date_params params
|
||||
|
||||
# -- Prepare form options
|
||||
my_distributors = Enterprise.is_distributor.managed_by(spree_current_user)
|
||||
@@ -117,18 +111,7 @@ Spree::Admin::ReportsController.class_eval do
|
||||
end
|
||||
|
||||
def orders_and_distributors
|
||||
params[:q] ||= {}
|
||||
|
||||
if params[:q][:completed_at_gt].blank?
|
||||
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
|
||||
else
|
||||
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
|
||||
end
|
||||
|
||||
if params[:q] && !params[:q][:completed_at_lt].blank?
|
||||
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue ""
|
||||
end
|
||||
params[:q][:meta_sort] ||= "completed_at.desc"
|
||||
prepare_date_params params
|
||||
|
||||
@search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q])
|
||||
orders = @search.result
|
||||
@@ -146,18 +129,7 @@ Spree::Admin::ReportsController.class_eval do
|
||||
end
|
||||
|
||||
def sales_tax
|
||||
params[:q] ||= {}
|
||||
|
||||
if params[:q][:completed_at_gt].blank?
|
||||
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
|
||||
else
|
||||
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
|
||||
end
|
||||
|
||||
if params[:q] && !params[:q][:completed_at_lt].blank?
|
||||
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue ""
|
||||
end
|
||||
params[:q][:meta_sort] ||= "completed_at.desc"
|
||||
prepare_date_params params
|
||||
|
||||
@search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q])
|
||||
orders = @search.result
|
||||
@@ -176,300 +148,47 @@ Spree::Admin::ReportsController.class_eval do
|
||||
end
|
||||
|
||||
def bulk_coop
|
||||
params[:q] ||= {}
|
||||
|
||||
if params[:q][:completed_at_gt].blank?
|
||||
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
|
||||
else
|
||||
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
|
||||
end
|
||||
|
||||
if params[:q] && !params[:q][:completed_at_lt].blank?
|
||||
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue ""
|
||||
end
|
||||
params[:q][:meta_sort] ||= "completed_at.desc"
|
||||
|
||||
@search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q])
|
||||
|
||||
orders = @search.result
|
||||
@line_items = orders.map { |o| o.line_items.managed_by(spree_current_user) }.flatten
|
||||
# -- Prepare date parameters
|
||||
prepare_date_params params
|
||||
|
||||
# -- Prepare form options
|
||||
@distributors = Enterprise.is_distributor.managed_by(spree_current_user)
|
||||
@report_type = params[:report_type]
|
||||
|
||||
case params[:report_type]
|
||||
when "bulk_coop_supplier_report"
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::BulkCoopReport.new spree_current_user, params
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
|
||||
@table = order_grouper.table(@report.table_items)
|
||||
csv_file_name = "bulk_coop_#{params[:report_type]}_#{timestamp}.csv"
|
||||
|
||||
header = ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"]
|
||||
|
||||
columns = [ proc { |lis| lis.first.variant.product.supplier.name },
|
||||
proc { |lis| lis.first.variant.product.name },
|
||||
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
|
||||
proc { |lis| lis.first.variant.full_name },
|
||||
proc { |lis| lis.first.variant.weight || 0 },
|
||||
proc { |lis| lis.sum { |li| li.quantity } },
|
||||
proc { |lis| lis.sum { |li| li.max_quantity || 0 } },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| "" } ]
|
||||
|
||||
rules = [ { group_by: proc { |li| li.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |li| li.variant.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [ proc { |lis| lis.first.variant.product.supplier.name },
|
||||
proc { |lis| lis.first.variant.product.name },
|
||||
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| lis.sum { |li| (li.quantity || 0) * (li.variant.weight || 0) } },
|
||||
proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } },
|
||||
proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor },
|
||||
proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] },
|
||||
{ group_by: proc { |li| li.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
|
||||
when "bulk_coop_allocation"
|
||||
|
||||
header = ["Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Allocated", "Remainder"]
|
||||
|
||||
columns = [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname },
|
||||
proc { |lis| lis.first.variant.product.name },
|
||||
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
|
||||
proc { |lis| lis.first.variant.full_name },
|
||||
proc { |lis| lis.first.variant.weight || 0 },
|
||||
proc { |lis| lis.sum { |li| li.quantity } },
|
||||
proc { |lis| lis.sum { |li| li.max_quantity || 0 } },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| "" } ]
|
||||
|
||||
rules = [ { group_by: proc { |li| li.variant.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [ proc { |lis| "TOTAL" },
|
||||
proc { |lis| lis.first.variant.product.name },
|
||||
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| lis.sum { |li| li.quantity * (li.variant.weight || 0) } },
|
||||
proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } },
|
||||
proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) },
|
||||
proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] },
|
||||
{ group_by: proc { |li| li.variant },
|
||||
sort_by: proc { |variant| variant.full_name } },
|
||||
{ group_by: proc { |li| li.order },
|
||||
sort_by: proc { |order| order.to_s } } ]
|
||||
|
||||
when "bulk_coop_packing_sheets"
|
||||
|
||||
header = ["Customer", "Product", "Variant", "Sum Total"]
|
||||
|
||||
columns = [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname },
|
||||
proc { |lis| lis.first.variant.product.name },
|
||||
proc { |lis| lis.first.variant.full_name },
|
||||
proc { |lis| lis.sum { |li| li.quantity } } ]
|
||||
|
||||
rules = [ { group_by: proc { |li| li.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |li| li.variant },
|
||||
sort_by: proc { |variant| variant.full_name } },
|
||||
{ group_by: proc { |li| li.order },
|
||||
sort_by: proc { |order| order.to_s } } ]
|
||||
|
||||
when "bulk_coop_customer_payments"
|
||||
|
||||
header = ["Customer", "Date of Order", "Total Cost", "Amount Owing", "Amount Paid"]
|
||||
|
||||
columns = [ 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 { |li| li.order }.uniq.sum { |o| o.total } },
|
||||
proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.outstanding_balance } },
|
||||
proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.payment_total } } ]
|
||||
|
||||
rules = [ { group_by: proc { |li| li.order },
|
||||
sort_by: proc { |order| order.completed_at } } ]
|
||||
|
||||
else # List all line items
|
||||
|
||||
header = ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"]
|
||||
|
||||
columns = [ proc { |lis| lis.first.variant.product.supplier.name },
|
||||
proc { |lis| lis.first.variant.product.name },
|
||||
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
|
||||
proc { |lis| lis.first.variant.full_name },
|
||||
proc { |lis| lis.first.variant.weight || 0 },
|
||||
proc { |lis| lis.sum { |li| li.quantity } },
|
||||
proc { |lis| lis.sum { |li| li.max_quantity || 0 } },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| "" } ]
|
||||
|
||||
rules = [ { group_by: proc { |li| li.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |li| li.variant.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [ proc { |lis| lis.first.variant.product.supplier.name },
|
||||
proc { |lis| lis.first.variant.product.name },
|
||||
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| lis.sum { |li| li.quantity * (li.variant.weight || 0) } },
|
||||
proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } },
|
||||
proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor },
|
||||
proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] },
|
||||
{ group_by: proc { |li| li.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
|
||||
end
|
||||
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns
|
||||
|
||||
@header = header
|
||||
@table = order_grouper.table(@line_items)
|
||||
csv_file_name = "bulk_coop_#{timestamp}.csv"
|
||||
|
||||
render_report(@header, @table, params[:csv], csv_file_name)
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def payments
|
||||
params[:q] ||= {}
|
||||
|
||||
if params[:q][:completed_at_gt].blank?
|
||||
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
|
||||
else
|
||||
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
|
||||
end
|
||||
|
||||
if params[:q] && !params[:q][:completed_at_lt].blank?
|
||||
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue ""
|
||||
end
|
||||
params[:q][:meta_sort] ||= "completed_at.desc"
|
||||
|
||||
@search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q])
|
||||
|
||||
orders = @search.result
|
||||
payments = orders.map { |o| o.payments.select { |payment| payment.completed? } }.flatten # Only select completed payments
|
||||
# -- Prepare Date Params
|
||||
prepare_date_params params
|
||||
|
||||
# -- Prepare Form Options
|
||||
@distributors = Enterprise.is_distributor.managed_by(spree_current_user)
|
||||
@report_type = params[:report_type]
|
||||
|
||||
case params[:report_type]
|
||||
when "payments_by_payment_type"
|
||||
table_items = payments
|
||||
|
||||
header = ["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"]
|
||||
|
||||
columns = [ proc { |payments| payments.first.order.payment_state },
|
||||
proc { |payments| payments.first.order.distributor.name },
|
||||
proc { |payments| payments.first.payment_method.name },
|
||||
proc { |payments| payments.sum { |payment| payment.amount } } ]
|
||||
|
||||
rules = [ { group_by: proc { |payment| payment.order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |payment| payment.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |payment| Spree::PaymentMethod.unscoped { payment.payment_method } },
|
||||
sort_by: proc { |method| method.name } } ]
|
||||
|
||||
when "itemised_payment_totals"
|
||||
table_items = orders
|
||||
|
||||
header = ["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})", "Total (#{currency_symbol})"]
|
||||
|
||||
columns = [ proc { |orders| orders.first.payment_state },
|
||||
proc { |orders| orders.first.distributor.name },
|
||||
proc { |orders| orders.sum { |o| o.item_total } },
|
||||
proc { |orders| orders.sum { |o| o.ship_total } },
|
||||
proc { |orders| orders.sum { |o| o.outstanding_balance } },
|
||||
proc { |orders| orders.sum { |o| o.total } } ]
|
||||
|
||||
rules = [ { group_by: proc { |order| order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |order| order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } } ]
|
||||
|
||||
when "payment_totals"
|
||||
table_items = orders
|
||||
|
||||
header = ["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Total (#{currency_symbol})", "EFT (#{currency_symbol})", "PayPal (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})"]
|
||||
|
||||
columns = [ proc { |orders| orders.first.payment_state },
|
||||
proc { |orders| orders.first.distributor.name },
|
||||
proc { |orders| orders.sum { |o| o.item_total } },
|
||||
proc { |orders| orders.sum { |o| o.ship_total } },
|
||||
proc { |orders| orders.sum { |o| o.total } },
|
||||
proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "EFT") }.sum { |payment| payment.amount } } },
|
||||
proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "PayPal") }.sum{ |payment| payment.amount } } },
|
||||
proc { |orders| orders.sum { |o| o.outstanding_balance } } ]
|
||||
|
||||
rules = [ { group_by: proc { |order| order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |order| order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } } ]
|
||||
|
||||
else
|
||||
table_items = payments
|
||||
|
||||
header = ["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"]
|
||||
|
||||
columns = [ proc { |payments| payments.first.order.payment_state },
|
||||
proc { |payments| payments.first.order.distributor.name },
|
||||
proc { |payments| payments.first.payment_method.name },
|
||||
proc { |payments| payments.sum { |payment| payment.amount } } ]
|
||||
|
||||
rules = [ { group_by: proc { |payment| payment.order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |payment| payment.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |payment| payment.payment_method },
|
||||
sort_by: proc { |method| method.name } } ]
|
||||
|
||||
end
|
||||
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns
|
||||
|
||||
@header = header
|
||||
@table = order_grouper.table(table_items)
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::PaymentsReport.new spree_current_user, params
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
|
||||
@table = order_grouper.table(@report.table_items)
|
||||
csv_file_name = "payments_#{timestamp}.csv"
|
||||
|
||||
render_report(@header, @table, params[:csv], csv_file_name)
|
||||
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def orders_and_fulfillment
|
||||
# -- Prepare parameters
|
||||
params[:q] ||= {}
|
||||
|
||||
if params[:q][:completed_at_gt].blank?
|
||||
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
|
||||
else
|
||||
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]) rescue Time.zone.now.beginning_of_month
|
||||
end
|
||||
|
||||
if params[:q] && !params[:q][:completed_at_lt].blank?
|
||||
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]) rescue ""
|
||||
end
|
||||
params[:q][:meta_sort] ||= "completed_at.desc"
|
||||
# -- Prepare Date Params
|
||||
prepare_date_params params
|
||||
|
||||
# -- Prepare Form Options
|
||||
permissions = OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
|
||||
# -- Search
|
||||
|
||||
@search = Spree::Order.complete.not_state(:canceled).search(params[:q])
|
||||
orders = permissions.visible_orders.merge(@search.result)
|
||||
|
||||
@line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders))
|
||||
@line_items = @line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present?
|
||||
|
||||
line_items_with_hidden_details = @line_items.where('"spree_line_items"."id" NOT IN (?)', permissions.editable_line_items)
|
||||
@line_items.select{ |li| line_items_with_hidden_details.include? li }.each do |line_item|
|
||||
# TODO We should really be hiding customer code here too, but until we
|
||||
# have an actual association between order and customer, it's a bit tricky
|
||||
line_item.order.bill_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil)
|
||||
line_item.order.ship_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil)
|
||||
line_item.order.assign_attributes(email: "HIDDEN")
|
||||
end
|
||||
|
||||
# My distributors and any distributors distributing products I supply
|
||||
@distributors = permissions.visible_enterprises_for_order_reports.is_distributor
|
||||
|
||||
# My suppliers and any suppliers supplying products I distribute
|
||||
@suppliers = permissions.visible_enterprises_for_order_reports.is_primary_producer
|
||||
|
||||
@@ -479,233 +198,15 @@ Spree::Admin::ReportsController.class_eval do
|
||||
@report_types = REPORT_TYPES[:orders_and_fulfillment]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Format according to report type
|
||||
case params[:report_type]
|
||||
when "order_cycle_supplier_totals"
|
||||
table_items = @line_items
|
||||
@include_blank = 'All'
|
||||
@include_blank = 'All'
|
||||
|
||||
header = ["Producer", "Product", "Variant", "Amount", "Total Units", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"]
|
||||
|
||||
columns = [ proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| total_units(line_items) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "incoming transport" } ]
|
||||
|
||||
rules = [ { group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
|
||||
when "order_cycle_supplier_totals_by_distributor"
|
||||
table_items = @line_items
|
||||
@include_blank = 'All'
|
||||
|
||||
header = ["Producer", "Product", "Variant", "To Hub", "Amount", "Curr. Cost per Unit", "Total Cost", "Shipping Method"]
|
||||
|
||||
columns = [ proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| "shipping method" } ]
|
||||
|
||||
rules = [ { group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name },
|
||||
summary_columns: [ proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "TOTAL" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| "" } ] },
|
||||
{ group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } } ]
|
||||
|
||||
when "order_cycle_distributor_totals_by_supplier"
|
||||
table_items = @line_items
|
||||
@include_blank = 'All'
|
||||
|
||||
header = ["Hub", "Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Total Shipping Cost", "Shipping Method"]
|
||||
|
||||
columns = [ proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "shipping method" } ]
|
||||
|
||||
rules = [ { group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name },
|
||||
summary_columns: [ proc { |line_items| "" },
|
||||
proc { |line_items| "TOTAL" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } },
|
||||
proc { |line_items| "" } ] },
|
||||
{ group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
|
||||
when "order_cycle_customer_totals"
|
||||
table_items = @line_items
|
||||
@include_blank = 'All'
|
||||
|
||||
header = ["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant",
|
||||
"Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Admin & Handling (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?",
|
||||
"Shipping", "Delivery?",
|
||||
"Ship Street", "Ship Street 2", "Ship City", "Ship Postcode", "Ship State",
|
||||
"Comments", "SKU",
|
||||
"Order Cycle", "Payment Method", "Customer Code", "Tags",
|
||||
"Billing Street 1", "Billing Street 2", "Billing City", "Billing Postcode", "Billing State"
|
||||
]
|
||||
|
||||
rsa = proc { |line_items| line_items.first.order.shipping_method.andand.require_ship_address }
|
||||
|
||||
columns = [
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname },
|
||||
proc { |line_items| line_items.first.order.email },
|
||||
proc { |line_items| line_items.first.order.bill_address.phone },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.shipping_method.andand.name },
|
||||
proc { |line_items| rsa.call(line_items) ? 'Y' : 'N' },
|
||||
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.address1 if rsa.call(line_items) },
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.address2 if rsa.call(line_items) },
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.city if rsa.call(line_items) },
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.zipcode if rsa.call(line_items) },
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.state if rsa.call(line_items) },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.first.variant.product.sku },
|
||||
|
||||
proc { |line_items| line_items.first.order.order_cycle.andand.name },
|
||||
proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name },
|
||||
proc { |line_items| line_items.first.order.user.andand.customer_of(line_items.first.order.distributor).andand.code },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.address1 },
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.address2 },
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.city },
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.zipcode },
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.state } ]
|
||||
|
||||
rules = [ { group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |line_item| line_item.order },
|
||||
sort_by: proc { |order| order.bill_address.lastname + " " + order.bill_address.firstname },
|
||||
summary_columns: [
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "TOTAL" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } },
|
||||
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.admin_and_handling_total } },
|
||||
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } },
|
||||
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.total } },
|
||||
proc { |line_items| line_items.all? { |li| li.order.paid? } ? "Yes" : "No" },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.special_instructions } ,
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.order_cycle.andand.name },
|
||||
proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" }
|
||||
] },
|
||||
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
|
||||
else
|
||||
table_items = @line_items
|
||||
@include_blank = 'All'
|
||||
|
||||
header = ["Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"]
|
||||
|
||||
columns = [ proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity * li.price } },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "incoming transport" } ]
|
||||
|
||||
rules = [ { group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
|
||||
end
|
||||
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns
|
||||
|
||||
@header = header
|
||||
@table = order_grouper.table(table_items)
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user, params
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
|
||||
@table = order_grouper.table(@report.table_items)
|
||||
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
|
||||
|
||||
render_report(@header, @table, params[:csv], csv_file_name)
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
|
||||
end
|
||||
|
||||
@@ -750,6 +251,20 @@ Spree::Admin::ReportsController.class_eval do
|
||||
|
||||
private
|
||||
|
||||
def prepare_date_params(params)
|
||||
# -- Prepare parameters
|
||||
params[:q] ||= {}
|
||||
if params[:q][:completed_at_gt].blank?
|
||||
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
|
||||
else
|
||||
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]) rescue Time.zone.now.beginning_of_month
|
||||
end
|
||||
if params[:q] && !params[:q][:completed_at_lt].blank?
|
||||
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]) rescue ""
|
||||
end
|
||||
params[:q][:meta_sort] ||= "completed_at.desc"
|
||||
end
|
||||
|
||||
def load_data
|
||||
# Load distributors either owned by the user or selling their enterprises products.
|
||||
my_distributors = Enterprise.is_distributor.managed_by(spree_current_user)
|
||||
@@ -781,15 +296,6 @@ Spree::Admin::ReportsController.class_eval do
|
||||
reports.select { |action| can? action, :report }
|
||||
end
|
||||
|
||||
def total_units(line_items)
|
||||
return " " if line_items.map{ |li| li.variant.unit_value.nil? }.any?
|
||||
total_units = line_items.sum do |li|
|
||||
scale_factor = ( li.product.variant_unit == 'weight' ? 1000 : 1 )
|
||||
li.quantity * li.variant.unit_value / scale_factor
|
||||
end
|
||||
total_units.round(3)
|
||||
end
|
||||
|
||||
def timestamp
|
||||
Time.now.strftime("%Y%m%d")
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= form_for @search, :url => spree.bulk_coop_admin_reports_path do |f|
|
||||
= form_for @report.search, :url => spree.bulk_coop_admin_reports_path do |f|
|
||||
= render 'date_range_form', f: f
|
||||
|
||||
.row
|
||||
@@ -20,7 +20,7 @@
|
||||
%table#listing_orders.index
|
||||
%thead
|
||||
%tr{'data-hook' => "orders_header"}
|
||||
- @header.each do |heading|
|
||||
- @report.header.each do |heading|
|
||||
%th=heading
|
||||
%tbody
|
||||
- @table.each do |row|
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= form_for @search, :url => spree.orders_and_fulfillment_admin_reports_path do |f|
|
||||
= form_for @report.search, :url => spree.orders_and_fulfillment_admin_reports_path do |f|
|
||||
= render 'date_range_form', f: f
|
||||
|
||||
.row
|
||||
@@ -30,7 +30,7 @@
|
||||
%table#listing_orders.index
|
||||
%thead
|
||||
%tr{'data-hook' => "orders_header"}
|
||||
- @header.each do |heading|
|
||||
- @report.header.each do |heading|
|
||||
%th=heading
|
||||
%tbody
|
||||
- @table.each do |row|
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= form_for @search, :url => spree.payments_admin_reports_path do |f|
|
||||
= form_for @report.search, :url => spree.payments_admin_reports_path do |f|
|
||||
= render 'date_range_form', f: f
|
||||
|
||||
.row
|
||||
@@ -20,7 +20,7 @@
|
||||
%table#listing_orders.index
|
||||
%thead
|
||||
%tr{'data-hook' => "orders_header"}
|
||||
- @header.each do |heading|
|
||||
- @report.header.each do |heading|
|
||||
%th=heading
|
||||
%tbody
|
||||
- @table.each do |row|
|
||||
|
||||
104
lib/open_food_network/bulk_coop_report.rb
Normal file
104
lib/open_food_network/bulk_coop_report.rb
Normal file
@@ -0,0 +1,104 @@
|
||||
require 'open_food_network/reports/bulk_coop_supplier_report'
|
||||
require 'open_food_network/reports/bulk_coop_allocation_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
class BulkCoopReport
|
||||
attr_reader :params
|
||||
def initialize(user, params = {})
|
||||
@params = params
|
||||
@user = user
|
||||
|
||||
@supplier_report = OpenFoodNetwork::Reports::BulkCoopSupplierReport.new
|
||||
@allocation_report = OpenFoodNetwork::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"
|
||||
["Customer", "Product", "Variant", "Sum Total"]
|
||||
when "bulk_coop_customer_payments"
|
||||
["Customer", "Date of Order", "Total Cost", "Amount Owing", "Amount Paid"]
|
||||
else
|
||||
["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
Spree::Order.complete.not_state(:canceled).managed_by(@user).search(params[:q])
|
||||
end
|
||||
|
||||
def table_items
|
||||
orders = search.result
|
||||
orders.map { |o| o.line_items.managed_by(@user) }.flatten
|
||||
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.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |li| li.variant },
|
||||
sort_by: proc { |variant| variant.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.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |li| li.variant.product },
|
||||
sort_by: proc { |product| product.name },
|
||||
summary_columns: [ proc { |lis| lis.first.variant.product.supplier.name },
|
||||
proc { |lis| lis.first.variant.product.name },
|
||||
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| lis.sum { |li| li.quantity * (li.variant.weight || 0) } },
|
||||
proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } },
|
||||
proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor },
|
||||
proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] },
|
||||
{ group_by: proc { |li| li.variant },
|
||||
sort_by: proc { |variant| variant.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.variant.product.name },
|
||||
proc { |lis| lis.first.variant.full_name },
|
||||
proc { |lis| lis.sum { |li| li.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 { |li| li.order }.uniq.sum { |o| o.total } },
|
||||
proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.outstanding_balance } },
|
||||
proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.payment_total } } ]
|
||||
else
|
||||
[ proc { |lis| lis.first.variant.product.supplier.name },
|
||||
proc { |lis| lis.first.variant.product.name },
|
||||
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
|
||||
proc { |lis| lis.first.variant.full_name },
|
||||
proc { |lis| lis.first.variant.weight || 0 },
|
||||
proc { |lis| lis.sum { |li| li.quantity } },
|
||||
proc { |lis| lis.sum { |li| li.max_quantity || 0 } },
|
||||
proc { |lis| "" },
|
||||
proc { |lis| "" } ]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -15,6 +15,17 @@ module OpenFoodNetwork
|
||||
name_fields.join ' '
|
||||
end
|
||||
|
||||
def value
|
||||
value, _ = option_value_value_unit
|
||||
value
|
||||
end
|
||||
|
||||
def unit
|
||||
_, unit = option_value_value_unit
|
||||
unit
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def value_scaled?
|
||||
|
||||
@@ -8,7 +8,7 @@ module OpenFoodNetwork
|
||||
|
||||
def build_tree(items, remaining_rules)
|
||||
rules = remaining_rules.clone
|
||||
unless rules.empty?
|
||||
if rules.any?
|
||||
rule = rules.delete_at(0) # Remove current rule for subsequent groupings
|
||||
group_and_sort(rule, rules, items)
|
||||
else
|
||||
@@ -20,7 +20,7 @@ module OpenFoodNetwork
|
||||
branch = {}
|
||||
groups = items.group_by { |item| rule[:group_by].call(item) }
|
||||
sorted_groups = groups.sort_by { |key, value| rule[:sort_by].call(key) }
|
||||
sorted_groups.each do |property,items_by_property|
|
||||
sorted_groups.each do |property, items_by_property|
|
||||
branch[property] = build_tree(items_by_property, remaining_rules)
|
||||
branch[property][:summary_row] = { items: items_by_property, columns: rule[:summary_columns] } unless rule[:summary_columns] == nil || is_leaf_node(branch[property])
|
||||
end
|
||||
@@ -44,15 +44,15 @@ module OpenFoodNetwork
|
||||
end
|
||||
|
||||
def table(items)
|
||||
tree = build_tree(items,@rules)
|
||||
tree = build_tree(items, @rules)
|
||||
table = build_table(tree)
|
||||
table
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
|
||||
def is_leaf_node(node)
|
||||
node.is_a? Array
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
256
lib/open_food_network/orders_and_fulfillments_report.rb
Normal file
256
lib/open_food_network/orders_and_fulfillments_report.rb
Normal file
@@ -0,0 +1,256 @@
|
||||
include Spree::ReportsHelper
|
||||
|
||||
module OpenFoodNetwork
|
||||
class OrdersAndFulfillmentsReport
|
||||
attr_reader :params
|
||||
def initialize(user, params = {})
|
||||
@params = params
|
||||
@user = user
|
||||
end
|
||||
|
||||
def header
|
||||
case params[:report_type]
|
||||
when "order_cycle_supplier_totals"
|
||||
["Producer", "Product", "Variant", "Amount", "Total Units", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"]
|
||||
when "order_cycle_supplier_totals_by_distributor"
|
||||
["Producer", "Product", "Variant", "To Hub", "Amount", "Curr. Cost per Unit", "Total Cost", "Shipping Method"]
|
||||
when "order_cycle_distributor_totals_by_supplier"
|
||||
["Hub", "Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Total Shipping Cost", "Shipping Method"]
|
||||
when "order_cycle_customer_totals"
|
||||
["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant",
|
||||
"Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Admin & Handling (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?",
|
||||
"Shipping", "Delivery?",
|
||||
"Ship Street", "Ship Street 2", "Ship City", "Ship Postcode", "Ship State",
|
||||
"Comments", "SKU",
|
||||
"Order Cycle", "Payment Method", "Customer Code", "Tags",
|
||||
"Billing Street 1", "Billing Street 2", "Billing City", "Billing Postcode", "Billing State"
|
||||
]
|
||||
else
|
||||
["Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def search
|
||||
Spree::Order.complete.not_state(:canceled).search(params[:q])
|
||||
end
|
||||
|
||||
def table_items
|
||||
permissions = OpenFoodNetwork::Permissions.new(@user)
|
||||
orders = permissions.visible_orders.merge(search.result)
|
||||
|
||||
line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders))
|
||||
line_items = line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present?
|
||||
|
||||
line_items_with_hidden_details = line_items.where('"spree_line_items"."id" NOT IN (?)', permissions.editable_line_items)
|
||||
line_items.select{ |li| line_items_with_hidden_details.include? li }.each do |line_item|
|
||||
# TODO We should really be hiding customer code here too, but until we
|
||||
# have an actual association between order and customer, it's a bit tricky
|
||||
line_item.order.bill_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil)
|
||||
line_item.order.ship_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil)
|
||||
line_item.order.assign_attributes(email: "HIDDEN")
|
||||
end
|
||||
line_items
|
||||
end
|
||||
|
||||
def rules
|
||||
case params[:report_type]
|
||||
when "order_cycle_supplier_totals"
|
||||
[ { group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
when "order_cycle_supplier_totals_by_distributor"
|
||||
[ { group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name },
|
||||
summary_columns: [ proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "TOTAL" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| "" } ] },
|
||||
{ group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } } ]
|
||||
when "order_cycle_distributor_totals_by_supplier"
|
||||
[ { group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name },
|
||||
summary_columns: [ proc { |line_items| "" },
|
||||
proc { |line_items| "TOTAL" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } },
|
||||
proc { |line_items| "" } ] },
|
||||
{ group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
when "order_cycle_customer_totals"
|
||||
[ { group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |line_item| line_item.order },
|
||||
sort_by: proc { |order| order.bill_address.lastname + " " + order.bill_address.firstname },
|
||||
summary_columns: [
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "TOTAL" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } },
|
||||
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.admin_and_handling_total } },
|
||||
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } },
|
||||
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.total } },
|
||||
proc { |line_items| line_items.all? { |li| li.order.paid? } ? "Yes" : "No" },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.special_instructions } ,
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.order_cycle.andand.name },
|
||||
proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" }
|
||||
] },
|
||||
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
else
|
||||
[ { group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name } },
|
||||
{ group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name } } ]
|
||||
end
|
||||
end
|
||||
|
||||
def columns
|
||||
case params[:report_type]
|
||||
when "order_cycle_supplier_totals"
|
||||
[ proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| total_units(line_items) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "incoming transport" } ]
|
||||
when "order_cycle_supplier_totals_by_distributor"
|
||||
[ proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| "shipping method" } ]
|
||||
when "order_cycle_distributor_totals_by_supplier"
|
||||
[ proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "shipping method" } ]
|
||||
when "order_cycle_customer_totals"
|
||||
rsa = proc { |line_items| line_items.first.order.shipping_method.andand.require_ship_address }
|
||||
[
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname },
|
||||
proc { |line_items| line_items.first.order.email },
|
||||
proc { |line_items| line_items.first.order.bill_address.phone },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| line_items.sum { |li| li.amount } },
|
||||
proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.shipping_method.andand.name },
|
||||
proc { |line_items| rsa.call(line_items) ? 'Y' : 'N' },
|
||||
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.address1 if rsa.call(line_items) },
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.address2 if rsa.call(line_items) },
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.city if rsa.call(line_items) },
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.zipcode if rsa.call(line_items) },
|
||||
proc { |line_items| line_items.first.order.ship_address.andand.state if rsa.call(line_items) },
|
||||
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| line_items.first.variant.product.sku },
|
||||
|
||||
proc { |line_items| line_items.first.order.order_cycle.andand.name },
|
||||
proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name },
|
||||
proc { |line_items| line_items.first.order.user.andand.customer_of(line_items.first.order.distributor).andand.code },
|
||||
proc { |line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.address1 },
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.address2 },
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.city },
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.zipcode },
|
||||
proc { |line_items| line_items.first.order.bill_address.andand.state } ]
|
||||
else
|
||||
[ proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity } },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity * li.price } },
|
||||
proc { |line_items| "" },
|
||||
proc { |line_items| "incoming transport" } ]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def total_units(line_items)
|
||||
return " " if line_items.map{ |li| li.variant.unit_value.nil? }.any?
|
||||
total_units = line_items.sum do |li|
|
||||
scale_factor = ( li.product.variant_unit == 'weight' ? 1000 : 1 )
|
||||
li.quantity * li.variant.unit_value / scale_factor
|
||||
end
|
||||
total_units.round(3)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
101
lib/open_food_network/payments_report.rb
Normal file
101
lib/open_food_network/payments_report.rb
Normal file
@@ -0,0 +1,101 @@
|
||||
module OpenFoodNetwork
|
||||
class PaymentsReport
|
||||
attr_reader :params
|
||||
def initialize(user, params = {})
|
||||
@params = params
|
||||
@user = user
|
||||
end
|
||||
|
||||
def header
|
||||
case params[:report_type]
|
||||
when "payments_by_payment_type"
|
||||
["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"]
|
||||
when "itemised_payment_totals"
|
||||
["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})", "Total (#{currency_symbol})"]
|
||||
when "payment_totals"
|
||||
["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Total (#{currency_symbol})", "EFT (#{currency_symbol})", "PayPal (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})"]
|
||||
else
|
||||
["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
Spree::Order.complete.not_state(:canceled).managed_by(@user).search(params[:q])
|
||||
end
|
||||
|
||||
def table_items
|
||||
orders = search.result
|
||||
payments = orders.map { |o| o.payments.select { |payment| payment.completed? } }.flatten # Only select completed payments
|
||||
case params[:report_type]
|
||||
when "payments_by_payment_type"
|
||||
payments
|
||||
when "itemised_payment_totals"
|
||||
orders
|
||||
when "payment_totals"
|
||||
orders
|
||||
else
|
||||
payments
|
||||
end
|
||||
end
|
||||
|
||||
def rules
|
||||
case params[:report_type]
|
||||
when "payments_by_payment_type"
|
||||
[ { group_by: proc { |payment| payment.order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |payment| payment.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |payment| Spree::PaymentMethod.unscoped { payment.payment_method } },
|
||||
sort_by: proc { |method| method.name } } ]
|
||||
when "itemised_payment_totals"
|
||||
[ { group_by: proc { |order| order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |order| order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } } ]
|
||||
when "payment_totals"
|
||||
[ { group_by: proc { |order| order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |order| order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } } ]
|
||||
else
|
||||
[ { group_by: proc { |payment| payment.order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |payment| payment.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |payment| payment.payment_method },
|
||||
sort_by: proc { |method| method.name } } ]
|
||||
end
|
||||
end
|
||||
|
||||
def columns
|
||||
case params[:report_type]
|
||||
when "payments_by_payment_type"
|
||||
[ proc { |payments| payments.first.order.payment_state },
|
||||
proc { |payments| payments.first.order.distributor.name },
|
||||
proc { |payments| payments.first.payment_method.name },
|
||||
proc { |payments| payments.sum { |payment| payment.amount } } ]
|
||||
when "itemised_payment_totals"
|
||||
[ proc { |orders| orders.first.payment_state },
|
||||
proc { |orders| orders.first.distributor.name },
|
||||
proc { |orders| orders.sum { |o| o.item_total } },
|
||||
proc { |orders| orders.sum { |o| o.ship_total } },
|
||||
proc { |orders| orders.sum { |o| o.outstanding_balance } },
|
||||
proc { |orders| orders.sum { |o| o.total } } ]
|
||||
when "payment_totals"
|
||||
[ proc { |orders| orders.first.payment_state },
|
||||
proc { |orders| orders.first.distributor.name },
|
||||
proc { |orders| orders.sum { |o| o.item_total } },
|
||||
proc { |orders| orders.sum { |o| o.ship_total } },
|
||||
proc { |orders| orders.sum { |o| o.total } },
|
||||
proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "EFT") }.sum { |payment| payment.amount } } },
|
||||
proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "PayPal") }.sum{ |payment| payment.amount } } },
|
||||
proc { |orders| orders.sum { |o| o.outstanding_balance } } ]
|
||||
else
|
||||
[ proc { |payments| payments.first.order.payment_state },
|
||||
proc { |payments| payments.first.order.distributor.name },
|
||||
proc { |payments| payments.first.payment_method.name },
|
||||
proc { |payments| payments.sum { |payment| payment.amount } } ]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
50
lib/open_food_network/reports/bulk_coop_allocation_report.rb
Normal file
50
lib/open_food_network/reports/bulk_coop_allocation_report.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
require 'open_food_network/reports/bulk_coop_report'
|
||||
|
||||
module OpenFoodNetwork::Reports
|
||||
class BulkCoopAllocationReport < BulkCoopReport
|
||||
header "Customer", "Product", "Unit Size", "Variant", "Variant value", "Variant unit", "Weight", "Sum Total", "Total Available", "Unallocated", "Max quantity excess"
|
||||
|
||||
organise do
|
||||
group { |li| li.variant.product }
|
||||
sort &:name
|
||||
|
||||
summary_row do
|
||||
column { |lis| "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 { |li| li.variant }
|
||||
sort &:full_name
|
||||
|
||||
organise do
|
||||
group { |li| li.order }
|
||||
sort { |order| order.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.variant.product.name }
|
||||
column { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" }
|
||||
column { |lis| lis.first.variant.full_name }
|
||||
column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first.variant).value }
|
||||
column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first.variant).unit }
|
||||
column { |lis| lis.first.variant.weight || 0 }
|
||||
column { |lis| total_amount(lis) }
|
||||
column { |lis| "" }
|
||||
column { |lis| "" }
|
||||
column { |lis| "" }
|
||||
end
|
||||
end
|
||||
end
|
||||
70
lib/open_food_network/reports/bulk_coop_report.rb
Normal file
70
lib/open_food_network/reports/bulk_coop_report.rb
Normal file
@@ -0,0 +1,70 @@
|
||||
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)
|
||||
if lis.first.variant.product.group_buy
|
||||
group_buy_unit_size(lis)
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def total_amount(lis)
|
||||
lis.sum { |li| (li.quantity || 0) * scaled_amount(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_amount(li)
|
||||
end
|
||||
end
|
||||
|
||||
def scaled_amount(li)
|
||||
(li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
50
lib/open_food_network/reports/bulk_coop_supplier_report.rb
Normal file
50
lib/open_food_network/reports/bulk_coop_supplier_report.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
require 'open_food_network/reports/bulk_coop_report'
|
||||
|
||||
module OpenFoodNetwork::Reports
|
||||
class BulkCoopSupplierReport < BulkCoopReport
|
||||
header "Supplier", "Product", "Unit Size", "Variant", "Variant value", "Variant unit", "Weight", "Sum Total", "Units Required", "Unallocated", "Max quantity excess"
|
||||
|
||||
organise do
|
||||
group { |li| li.variant.product.supplier }
|
||||
sort &:name
|
||||
|
||||
organise do
|
||||
group { |li| li.variant.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 { |li| li.variant }
|
||||
sort &: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.variant.full_name }
|
||||
column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first.variant).value }
|
||||
column { |lis| OpenFoodNetwork::OptionValueNamer.new(lis.first.variant).unit }
|
||||
column { |lis| lis.first.variant.weight || 0 }
|
||||
column { |lis| total_amount(lis) }
|
||||
column { |lis| '' }
|
||||
column { |lis| '' }
|
||||
column { |lis| '' }
|
||||
end
|
||||
end
|
||||
end
|
||||
46
lib/open_food_network/reports/report.rb
Normal file
46
lib/open_food_network/reports/report.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
require 'open_food_network/reports/row'
|
||||
require 'open_food_network/reports/rule'
|
||||
|
||||
module OpenFoodNetwork::Reports
|
||||
class Report
|
||||
class_attribute :_header, :_columns, :_rules_head
|
||||
|
||||
# -- API
|
||||
def header
|
||||
self._header
|
||||
end
|
||||
|
||||
def columns
|
||||
self._columns.to_a
|
||||
end
|
||||
|
||||
def rules
|
||||
# Flatten linked list and return as hashes
|
||||
rules = []
|
||||
|
||||
rule = self._rules_head
|
||||
while rule
|
||||
rules << rule
|
||||
rule = rule.next
|
||||
end
|
||||
|
||||
rules.map &:to_h
|
||||
end
|
||||
|
||||
|
||||
# -- DSL
|
||||
def self.header(*columns)
|
||||
self._header = columns
|
||||
end
|
||||
|
||||
def self.columns(&block)
|
||||
self._columns = Row.new
|
||||
Blockenspiel.invoke block, self._columns
|
||||
end
|
||||
|
||||
def self.organise(&block)
|
||||
self._rules_head = Rule.new
|
||||
Blockenspiel.invoke block, self._rules_head
|
||||
end
|
||||
end
|
||||
end
|
||||
17
lib/open_food_network/reports/row.rb
Normal file
17
lib/open_food_network/reports/row.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
module OpenFoodNetwork::Reports
|
||||
class Row
|
||||
include Blockenspiel::DSL
|
||||
|
||||
def initialize
|
||||
@columns = []
|
||||
end
|
||||
|
||||
def column(&block)
|
||||
@columns << block
|
||||
end
|
||||
|
||||
def to_a
|
||||
@columns
|
||||
end
|
||||
end
|
||||
end
|
||||
32
lib/open_food_network/reports/rule.rb
Normal file
32
lib/open_food_network/reports/rule.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
require 'open_food_network/reports/row'
|
||||
|
||||
module OpenFoodNetwork::Reports
|
||||
class Rule
|
||||
include Blockenspiel::DSL
|
||||
attr_reader :next
|
||||
|
||||
def group(&block)
|
||||
@group = block
|
||||
end
|
||||
|
||||
def sort(&block)
|
||||
@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.merge!({summary_columns: @summary_row.to_a}) if @summary_row
|
||||
h
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -56,6 +56,11 @@ describe Spree::Admin::ReportsController do
|
||||
order
|
||||
end
|
||||
|
||||
# Results
|
||||
let(:resulting_orders_prelim) { assigns(:report).search.result }
|
||||
let(:resulting_orders) { assigns(:report).table_items.map(&:order) }
|
||||
let(:resulting_products) { assigns(:report).table_items.map(&:product) }
|
||||
|
||||
# As manager of a coordinator (c1)
|
||||
context "Coordinator Enterprise User" do
|
||||
before { login_as_enterprise_user [c1] }
|
||||
@@ -64,8 +69,8 @@ describe Spree::Admin::ReportsController do
|
||||
it "shows all orders in order cycles I coordinate" do
|
||||
spree_get :orders_and_fulfillment
|
||||
|
||||
assigns(:line_items).map(&:order).should include orderA1, orderA2
|
||||
assigns(:line_items).map(&:order).should_not include orderB1, orderB2
|
||||
resulting_orders.should include orderA1, orderA2
|
||||
resulting_orders.should_not include orderB1, orderB2
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -88,9 +93,9 @@ describe Spree::Admin::ReportsController do
|
||||
it "only shows orders that I have access to" do
|
||||
spree_get :bulk_coop
|
||||
|
||||
assigns(:search).result.should include(orderA1, orderB1)
|
||||
assigns(:search).result.should_not include(orderA2)
|
||||
assigns(:search).result.should_not include(orderB2)
|
||||
resulting_orders.should include(orderA1, orderB1)
|
||||
resulting_orders.should_not include(orderA2)
|
||||
resulting_orders.should_not include(orderB2)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -98,9 +103,9 @@ describe Spree::Admin::ReportsController do
|
||||
it "only shows orders that I have access to" do
|
||||
spree_get :payments
|
||||
|
||||
assigns(:search).result.should include(orderA1, orderB1)
|
||||
assigns(:search).result.should_not include(orderA2)
|
||||
assigns(:search).result.should_not include(orderB2)
|
||||
resulting_orders_prelim.should include(orderA1, orderB1)
|
||||
resulting_orders_prelim.should_not include(orderA2)
|
||||
resulting_orders_prelim.should_not include(orderB2)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -108,15 +113,15 @@ describe Spree::Admin::ReportsController do
|
||||
it "only shows orders that I distribute" do
|
||||
spree_get :orders_and_fulfillment
|
||||
|
||||
assigns(:line_items).map(&:order).should include orderA1, orderB1
|
||||
assigns(:line_items).map(&:order).should_not include orderA2, orderB2
|
||||
resulting_orders.should include orderA1, orderB1
|
||||
resulting_orders.should_not include orderA2, orderB2
|
||||
end
|
||||
|
||||
it "only shows the selected order cycle" do
|
||||
spree_get :orders_and_fulfillment, q: {order_cycle_id_in: [ocA.id.to_s]}
|
||||
|
||||
assigns(:search).result.should include(orderA1)
|
||||
assigns(:search).result.should_not include(orderB1)
|
||||
resulting_orders.should include(orderA1)
|
||||
resulting_orders.should_not include(orderB1)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -129,8 +134,8 @@ describe Spree::Admin::ReportsController do
|
||||
it "only shows product line items that I am supplying" do
|
||||
spree_get :bulk_coop
|
||||
|
||||
assigns(:line_items).map(&:product).should include p1
|
||||
assigns(:line_items).map(&:product).should_not include p2, p3
|
||||
resulting_products.should include p1
|
||||
resulting_products.should_not include p2, p3
|
||||
end
|
||||
end
|
||||
|
||||
@@ -143,8 +148,8 @@ describe Spree::Admin::ReportsController do
|
||||
it "only shows product line items that I am supplying" do
|
||||
spree_get :orders_and_fulfillment
|
||||
|
||||
assigns(:line_items).map(&:product).should include p1
|
||||
assigns(:line_items).map(&:product).should_not include p2, p3
|
||||
resulting_products.should include p1
|
||||
resulting_products.should_not include p2, p3
|
||||
end
|
||||
end
|
||||
|
||||
@@ -152,15 +157,15 @@ describe Spree::Admin::ReportsController do
|
||||
it "does not show me line_items I supply" do
|
||||
spree_get :orders_and_fulfillment
|
||||
|
||||
assigns(:line_items).map(&:product).should_not include p1, p2, p3
|
||||
resulting_products.should_not include p1, p2, p3
|
||||
end
|
||||
end
|
||||
|
||||
it "only shows the selected order cycle" do
|
||||
spree_get :orders_and_fulfillment, q: {order_cycle_id_eq: ocA.id}
|
||||
|
||||
assigns(:search).result.should include(orderA1)
|
||||
assigns(:search).result.should_not include(orderB1)
|
||||
resulting_orders_prelim.should include(orderA1)
|
||||
resulting_orders_prelim.should_not include(orderB1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,8 @@ feature %q{
|
||||
let!(:invisible_producer) { create(:supplier_enterprise, visible: false) }
|
||||
let(:taxon) { create(:taxon) }
|
||||
let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) }
|
||||
let(:shop) { create(:distributor_enterprise) }
|
||||
let!(:er) { create(:enterprise_relationship, parent: shop, child: producer) }
|
||||
|
||||
before do
|
||||
visit producers_path
|
||||
@@ -24,4 +26,9 @@ feature %q{
|
||||
it "doesn't show invisible producers" do
|
||||
page.should_not have_content invisible_producer.name
|
||||
end
|
||||
|
||||
it "links to places to buy produce" do
|
||||
expand_active_table_node producer.name
|
||||
page.should have_link shop.name
|
||||
end
|
||||
end
|
||||
|
||||
100
spec/lib/open_food_network/reports/report_spec.rb
Normal file
100
spec/lib/open_food_network/reports/report_spec.rb
Normal file
@@ -0,0 +1,100 @@
|
||||
require 'open_food_network/reports/report'
|
||||
|
||||
module OpenFoodNetwork::Reports
|
||||
P1 = Proc.new { |o| o[:one] }
|
||||
P2 = Proc.new { |o| o[:two] }
|
||||
P3 = Proc.new { |o| o[:three] }
|
||||
P4 = Proc.new { |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 { |o| my_helper(o) }
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def self.my_helper(o)
|
||||
o[: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
|
||||
report.header.should == %w(One Two Three Four)
|
||||
end
|
||||
|
||||
it "returns columns as an array of procs" do
|
||||
report.columns[0].call(data).should == 1
|
||||
report.columns[1].call(data).should == 2
|
||||
report.columns[2].call(data).should == 3
|
||||
report.columns[3].call(data).should == 4
|
||||
end
|
||||
|
||||
it "supports helpers when outputting columns" do
|
||||
helper_report.columns[0].call(data).should == 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
|
||||
group_by.call(data).should == 1
|
||||
sort_by.call(data).should == 2
|
||||
end
|
||||
|
||||
it "constructs nested rules" do
|
||||
next_group_by.call(data).should == 3
|
||||
next_sort_by.call(data).should == 4
|
||||
end
|
||||
|
||||
it "constructs summary columns for rules" do
|
||||
next_summary_columns[0].call(data).should == 1
|
||||
next_summary_columns[1].call(data).should == 4
|
||||
end
|
||||
end
|
||||
|
||||
describe "outputting rules" do
|
||||
it "outputs the rules" do
|
||||
report.rules.should == [{group_by: P1, sort_by: P2},
|
||||
{group_by: P3, sort_by: P4, summary_columns: [P1, P4]}]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
16
spec/lib/open_food_network/reports/row_spec.rb
Normal file
16
spec/lib/open_food_network/reports/row_spec.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
require 'open_food_network/reports/row'
|
||||
|
||||
module OpenFoodNetwork::Reports
|
||||
describe Row do
|
||||
let(:row) { Row.new }
|
||||
let(:proc) { Proc.new {} }
|
||||
|
||||
it "can define a number of columns and return them as an array" do
|
||||
row.column &proc
|
||||
row.column &proc
|
||||
row.column &proc
|
||||
|
||||
row.to_a.should == [proc, proc, proc]
|
||||
end
|
||||
end
|
||||
end
|
||||
36
spec/lib/open_food_network/reports/rule_spec.rb
Normal file
36
spec/lib/open_food_network/reports/rule_spec.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
require 'open_food_network/reports/rule'
|
||||
|
||||
module OpenFoodNetwork::Reports
|
||||
describe Rule do
|
||||
let(:rule) { Rule.new }
|
||||
let(:proc) { Proc.new {} }
|
||||
|
||||
it "can define a group proc and return it in a hash" do
|
||||
rule.group &proc
|
||||
rule.to_h.should == {group_by: proc, sort_by: nil}
|
||||
end
|
||||
|
||||
it "can define a sort proc and return it in a hash" do
|
||||
rule.sort &proc
|
||||
rule.to_h.should == {group_by: nil, sort_by: proc}
|
||||
end
|
||||
|
||||
it "can define a nested rule" do
|
||||
rule.organise &proc
|
||||
rule.next.should 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
|
||||
|
||||
rule.to_h[:summary_columns].count.should == 3
|
||||
rule.to_h[:summary_columns][0].should be_a Proc
|
||||
rule.to_h[:summary_columns][1].should be_a Proc
|
||||
rule.to_h[:summary_columns][2].should be_a Proc
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user