From e679f1175ca529977017e625c36e038f2c66c750 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 9 Jul 2015 16:45:34 +1000 Subject: [PATCH 01/38] Add shop-for-products-at to map producer modals --- .../templates/enterprise_modal.html.haml | 1 + .../partials/producer_details.html.haml | 20 +++++++++++++++++++ spec/features/consumer/producers_spec.rb | 7 +++++++ 3 files changed, 28 insertions(+) create mode 100644 app/assets/javascripts/templates/partials/producer_details.html.haml diff --git a/app/assets/javascripts/templates/enterprise_modal.html.haml b/app/assets/javascripts/templates/enterprise_modal.html.haml index 6b13f6ee78..350cd6ed80 100644 --- a/app/assets/javascripts/templates/enterprise_modal.html.haml +++ b/app/assets/javascripts/templates/enterprise_modal.html.haml @@ -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'"} diff --git a/app/assets/javascripts/templates/partials/producer_details.html.haml b/app/assets/javascripts/templates/partials/producer_details.html.haml new file mode 100644 index 0000000000..aea4fbfa38 --- /dev/null +++ b/app/assets/javascripts/templates/partials/producer_details.html.haml @@ -0,0 +1,20 @@ +.row.active_table_row.pad-top{bindonce: true, "ng-if" => "enterprise.is_primary_producer && enterprise.hubs"} + .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 | 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"} diff --git a/spec/features/consumer/producers_spec.rb b/spec/features/consumer/producers_spec.rb index 6e09365195..8a26eaf026 100644 --- a/spec/features/consumer/producers_spec.rb +++ b/spec/features/consumer/producers_spec.rb @@ -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 From 3527ae6ea21b1f4e2bc44356d398e057df4a9665 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Jul 2015 11:42:13 +1000 Subject: [PATCH 02/38] Update comment --- app/assets/javascripts/darkswarm/services/map.js.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/map.js.coffee b/app/assets/javascripts/darkswarm/services/map.js.coffee index 7ff9f553f2..96768e8379 100644 --- a/app/assets/javascripts/darkswarm/services/map.js.coffee +++ b/app/assets/javascripts/darkswarm/services/map.js.coffee @@ -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 From a509747ba7d44b605fcd1c2521be95ce62b754f7 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 10 Jul 2015 11:52:45 +1000 Subject: [PATCH 03/38] Do not show double-ups of places to shop --- .../templates/partials/producer_details.html.haml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/templates/partials/producer_details.html.haml b/app/assets/javascripts/templates/partials/producer_details.html.haml index aea4fbfa38..08ac6ea1f1 100644 --- a/app/assets/javascripts/templates/partials/producer_details.html.haml +++ b/app/assets/javascripts/templates/partials/producer_details.html.haml @@ -1,4 +1,7 @@ -.row.active_table_row.pad-top{bindonce: true, "ng-if" => "enterprise.is_primary_producer && enterprise.hubs"} +-# 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 @@ -11,7 +14,7 @@   .row.cta-container .columns.small-12 - %a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | orderBy:'-active'", + %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"} From 3a0c5bf0c3ccc66cd72c609b82973ae4bb51de18 Mon Sep 17 00:00:00 2001 From: Myriam Date: Sun, 19 Jul 2015 12:09:20 +0200 Subject: [PATCH 04/38] I changed the "Get it" link as the git@ one was reserved to people with access rights --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index b9aaf8193c..baaba4b6bb 100644 --- a/README.markdown +++ b/README.markdown @@ -32,7 +32,7 @@ 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 From 5f0766cb7534737cee0af23ba8153774a32deab2 Mon Sep 17 00:00:00 2001 From: Myriam Date: Sun, 19 Jul 2015 12:37:23 +0200 Subject: [PATCH 05/38] When installing the gems we had only 1.9.2, there were an imprecision with >=1.9.3 or 1.9.x --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index baaba4b6bb..c6c83dab42 100644 --- a/README.markdown +++ b/README.markdown @@ -39,7 +39,7 @@ You can download the source with the command: 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 From c15e28128651a9d93acf757285b7a95dff785361 Mon Sep 17 00:00:00 2001 From: Myriam Date: Sun, 19 Jul 2015 12:39:51 +0200 Subject: [PATCH 06/38] Add command cd openfoodnetwork Just added the command to go into the openfoodnetwork folder before bundle install --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index c6c83dab42..b8c5f04e61 100644 --- a/README.markdown +++ b/README.markdown @@ -45,6 +45,7 @@ First, check your dependencies: Ensure that you have Ruby >= 1.9.3 installed: Install the project's gem dependencies: + cd openfoodnetwork bundle install Configure the site: From eabf7922380d8a8fa29d59519b93f8de404af967 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 22 Jul 2015 13:16:26 +1000 Subject: [PATCH 07/38] Amend changes to readme --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index b8c5f04e61..afb234c7fb 100644 --- a/README.markdown +++ b/README.markdown @@ -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 From 61a39ea82f5650e8a727b21965035e9237c1b3ba Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 22 Jul 2015 15:23:53 +1000 Subject: [PATCH 08/38] Cherry-pick 00927f7: Refactoring to move bulk out of reports_controller_decorator --- .../admin/reports_controller_decorator.rb | 588 ++---------------- .../spree/admin/reports/bulk_coop.html.haml | 4 +- .../reports/orders_and_fulfillment.html.haml | 4 +- .../spree/admin/reports/payments.html.haml | 4 +- lib/open_food_network/bulk_coop_report.rb | 142 +++++ .../orders_and_fulfillments_report.rb | 256 ++++++++ lib/open_food_network/payments_report.rb | 101 +++ 7 files changed, 552 insertions(+), 547 deletions(-) create mode 100644 lib/open_food_network/bulk_coop_report.rb create mode 100644 lib/open_food_network/orders_and_fulfillments_report.rb create mode 100644 lib/open_food_network/payments_report.rb diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 78026fad1e..a597bce934 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -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 diff --git a/app/views/spree/admin/reports/bulk_coop.html.haml b/app/views/spree/admin/reports/bulk_coop.html.haml index d80a449bb4..8d9a1234ce 100644 --- a/app/views/spree/admin/reports/bulk_coop.html.haml +++ b/app/views/spree/admin/reports/bulk_coop.html.haml @@ -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| diff --git a/app/views/spree/admin/reports/orders_and_fulfillment.html.haml b/app/views/spree/admin/reports/orders_and_fulfillment.html.haml index 081cb43384..2465fe0ff2 100644 --- a/app/views/spree/admin/reports/orders_and_fulfillment.html.haml +++ b/app/views/spree/admin/reports/orders_and_fulfillment.html.haml @@ -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| diff --git a/app/views/spree/admin/reports/payments.html.haml b/app/views/spree/admin/reports/payments.html.haml index 1a92d507db..909846f47f 100644 --- a/app/views/spree/admin/reports/payments.html.haml +++ b/app/views/spree/admin/reports/payments.html.haml @@ -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| diff --git a/lib/open_food_network/bulk_coop_report.rb b/lib/open_food_network/bulk_coop_report.rb new file mode 100644 index 0000000000..1dc3be5a0e --- /dev/null +++ b/lib/open_food_network/bulk_coop_report.rb @@ -0,0 +1,142 @@ +module OpenFoodNetwork + class BulkCoopReport + attr_reader :params + def initialize(user, params = {}) + @params = params + @user = user + end + + def header + case params[:report_type] + when "bulk_coop_supplier_report" + ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"] + when "bulk_coop_allocation" + ["Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Allocated", "Remainder"] + 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" + [ { 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" + [ { 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" + [ { 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" + [ 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| "" } ] + when "bulk_coop_allocation" + [ 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| "" } ] + 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 diff --git a/lib/open_food_network/orders_and_fulfillments_report.rb b/lib/open_food_network/orders_and_fulfillments_report.rb new file mode 100644 index 0000000000..2d4e1d1f1f --- /dev/null +++ b/lib/open_food_network/orders_and_fulfillments_report.rb @@ -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 diff --git a/lib/open_food_network/payments_report.rb b/lib/open_food_network/payments_report.rb new file mode 100644 index 0000000000..4872da6b2f --- /dev/null +++ b/lib/open_food_network/payments_report.rb @@ -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 From 0a5e8fe6293333d1ed4d30828d5ee413d88b39a4 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 09:36:09 +1000 Subject: [PATCH 09/38] Report class can define and retrieve header --- lib/open_food_network/reports/report.rb | 14 ++++++++++++++ spec/lib/open_food_network/reports/report_spec.rb | 15 +++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 lib/open_food_network/reports/report.rb create mode 100644 spec/lib/open_food_network/reports/report_spec.rb diff --git a/lib/open_food_network/reports/report.rb b/lib/open_food_network/reports/report.rb new file mode 100644 index 0000000000..f92e268765 --- /dev/null +++ b/lib/open_food_network/reports/report.rb @@ -0,0 +1,14 @@ +module OpenFoodNetwork::Reports + class Report + # -- API + def header + @@header + end + + + # -- DSL + def self.header(*columns) + @@header = columns + end + end +end diff --git a/spec/lib/open_food_network/reports/report_spec.rb b/spec/lib/open_food_network/reports/report_spec.rb new file mode 100644 index 0000000000..4b64ea0f23 --- /dev/null +++ b/spec/lib/open_food_network/reports/report_spec.rb @@ -0,0 +1,15 @@ +require 'open_food_network/reports/report' + +module OpenFoodNetwork::Reports + class TestReport < Report + header 'One', 'Two', 'Three' + end + + describe Report do + let(:report) { TestReport.new } + + it "returns the header" do + report.header.should == %w(One Two Three) + end + end +end From c7a1ca29f47ce2a02e717ea3402310cecf6f2b5e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 09:52:59 +1000 Subject: [PATCH 10/38] Report can define and retrieve columns --- lib/open_food_network/reports/report.rb | 11 +++++++++++ lib/open_food_network/reports/row.rb | 16 ++++++++++++++++ .../lib/open_food_network/reports/report_spec.rb | 13 +++++++++++++ spec/lib/open_food_network/reports/row_spec.rb | 16 ++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 lib/open_food_network/reports/row.rb create mode 100644 spec/lib/open_food_network/reports/row_spec.rb diff --git a/lib/open_food_network/reports/report.rb b/lib/open_food_network/reports/report.rb index f92e268765..86495627fc 100644 --- a/lib/open_food_network/reports/report.rb +++ b/lib/open_food_network/reports/report.rb @@ -1,3 +1,5 @@ +require 'open_food_network/reports/row' + module OpenFoodNetwork::Reports class Report # -- API @@ -5,10 +7,19 @@ module OpenFoodNetwork::Reports @@header end + def columns + @@columns.to_a + end + # -- DSL def self.header(*columns) @@header = columns end + + def self.columns(&block) + @@columns = Row.new + @@columns.instance_eval(&block) + end end end diff --git a/lib/open_food_network/reports/row.rb b/lib/open_food_network/reports/row.rb new file mode 100644 index 0000000000..141564de94 --- /dev/null +++ b/lib/open_food_network/reports/row.rb @@ -0,0 +1,16 @@ +module OpenFoodNetwork::Reports + class Row + def initialize + @columns = [] + end + + + def column(&block) + @columns << block + end + + def to_a + @columns + end + end +end diff --git a/spec/lib/open_food_network/reports/report_spec.rb b/spec/lib/open_food_network/reports/report_spec.rb index 4b64ea0f23..4ee1e08a63 100644 --- a/spec/lib/open_food_network/reports/report_spec.rb +++ b/spec/lib/open_food_network/reports/report_spec.rb @@ -3,13 +3,26 @@ require 'open_food_network/reports/report' module OpenFoodNetwork::Reports class TestReport < Report header 'One', 'Two', 'Three' + + columns do + column { |o| o[:one] } + column { |o| o[:two] } + column { |o| o[:three] } + end end describe Report do let(:report) { TestReport.new } + let(:data) { {one: 1, two: 2, three: 3} } it "returns the header" do report.header.should == %w(One Two Three) 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 + end end end diff --git a/spec/lib/open_food_network/reports/row_spec.rb b/spec/lib/open_food_network/reports/row_spec.rb new file mode 100644 index 0000000000..deaa84e491 --- /dev/null +++ b/spec/lib/open_food_network/reports/row_spec.rb @@ -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 From 66f64fc4132a6e4ff5cd8aa07b7b7089bfe0c8e4 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 10:08:38 +1000 Subject: [PATCH 11/38] Report can define basic rules --- lib/open_food_network/reports/report.rb | 6 ++++++ lib/open_food_network/reports/rule.rb | 16 ++++++++++++++++ .../open_food_network/reports/report_spec.rb | 16 ++++++++++++++++ .../lib/open_food_network/reports/rule_spec.rb | 18 ++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 lib/open_food_network/reports/rule.rb create mode 100644 spec/lib/open_food_network/reports/rule_spec.rb diff --git a/lib/open_food_network/reports/report.rb b/lib/open_food_network/reports/report.rb index 86495627fc..ad7c09ae7c 100644 --- a/lib/open_food_network/reports/report.rb +++ b/lib/open_food_network/reports/report.rb @@ -1,4 +1,5 @@ require 'open_food_network/reports/row' +require 'open_food_network/reports/rule' module OpenFoodNetwork::Reports class Report @@ -21,5 +22,10 @@ module OpenFoodNetwork::Reports @@columns = Row.new @@columns.instance_eval(&block) end + + def self.organise(&block) + @@rules_head = Rule.new + @@rules_head.instance_eval(&block) + end end end diff --git a/lib/open_food_network/reports/rule.rb b/lib/open_food_network/reports/rule.rb new file mode 100644 index 0000000000..d65206766f --- /dev/null +++ b/lib/open_food_network/reports/rule.rb @@ -0,0 +1,16 @@ +module OpenFoodNetwork::Reports + class Rule + def group(&block) + @group = block + end + + def sort(&block) + @sort = block + end + + def to_h + {group_by: @group, sort_by: @sort} + end + + end +end diff --git a/spec/lib/open_food_network/reports/report_spec.rb b/spec/lib/open_food_network/reports/report_spec.rb index 4ee1e08a63..bbb5ad3dc3 100644 --- a/spec/lib/open_food_network/reports/report_spec.rb +++ b/spec/lib/open_food_network/reports/report_spec.rb @@ -9,10 +9,16 @@ module OpenFoodNetwork::Reports column { |o| o[:two] } column { |o| o[:three] } end + + organise do + group { |o| o[:one] } + sort { |o| o[:two] } + end end describe Report do let(:report) { TestReport.new } + let(:rules_head) { TestReport.class_variable_get(:@@rules_head) } let(:data) { {one: 1, two: 2, three: 3} } it "returns the header" do @@ -24,5 +30,15 @@ module OpenFoodNetwork::Reports report.columns[1].call(data).should == 2 report.columns[2].call(data).should == 3 end + + describe "rules" do + let(:group_by) { rules_head.to_h[:group_by] } + let(:sort_by) { rules_head.to_h[:sort_by] } + + it "constructs a linked list of rules" do + group_by.call(data).should == 1 + sort_by.call(data).should == 2 + end + end end end diff --git a/spec/lib/open_food_network/reports/rule_spec.rb b/spec/lib/open_food_network/reports/rule_spec.rb new file mode 100644 index 0000000000..d775d5ed48 --- /dev/null +++ b/spec/lib/open_food_network/reports/rule_spec.rb @@ -0,0 +1,18 @@ +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 + end +end From 07eb857a8d5cca50b4e5a785ad355be86578e67f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 10:24:10 +1000 Subject: [PATCH 12/38] Report can define nested rules --- lib/open_food_network/reports/rule.rb | 8 ++++++- .../open_food_network/reports/report_spec.rb | 22 +++++++++++++++---- .../open_food_network/reports/rule_spec.rb | 5 +++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/open_food_network/reports/rule.rb b/lib/open_food_network/reports/rule.rb index d65206766f..676af48ffc 100644 --- a/lib/open_food_network/reports/rule.rb +++ b/lib/open_food_network/reports/rule.rb @@ -1,5 +1,7 @@ module OpenFoodNetwork::Reports class Rule + attr_reader :next + def group(&block) @group = block end @@ -8,9 +10,13 @@ module OpenFoodNetwork::Reports @sort = block end + def organise(&block) + @next = Rule.new + @next.instance_eval &block + end + def to_h {group_by: @group, sort_by: @sort} end - end end diff --git a/spec/lib/open_food_network/reports/report_spec.rb b/spec/lib/open_food_network/reports/report_spec.rb index bbb5ad3dc3..72b64dbc28 100644 --- a/spec/lib/open_food_network/reports/report_spec.rb +++ b/spec/lib/open_food_network/reports/report_spec.rb @@ -2,43 +2,57 @@ require 'open_food_network/reports/report' module OpenFoodNetwork::Reports class TestReport < Report - header 'One', 'Two', 'Three' + header 'One', 'Two', 'Three', 'Four' columns do column { |o| o[:one] } column { |o| o[:two] } column { |o| o[:three] } + column { |o| o[:four] } end organise do group { |o| o[:one] } sort { |o| o[:two] } + + organise do + group { |o| o[:three] } + sort { |o| o[:four] } + end end end describe Report do let(:report) { TestReport.new } let(:rules_head) { TestReport.class_variable_get(:@@rules_head) } - let(:data) { {one: 1, two: 2, three: 3} } + let(:data) { {one: 1, two: 2, three: 3, four: 4} } it "returns the header" do - report.header.should == %w(One Two Three) + 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 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] } - it "constructs a linked list of rules" do + 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 end end end diff --git a/spec/lib/open_food_network/reports/rule_spec.rb b/spec/lib/open_food_network/reports/rule_spec.rb index d775d5ed48..16a7e49d0d 100644 --- a/spec/lib/open_food_network/reports/rule_spec.rb +++ b/spec/lib/open_food_network/reports/rule_spec.rb @@ -14,5 +14,10 @@ module OpenFoodNetwork::Reports 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 end end From 1d39fb4438f1923bb97234341a11db5d703cea0b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 10:35:40 +1000 Subject: [PATCH 13/38] Report rules can define a summary row --- lib/open_food_network/reports/rule.rb | 11 ++++++++++- spec/lib/open_food_network/reports/report_spec.rb | 11 +++++++++++ spec/lib/open_food_network/reports/rule_spec.rb | 13 +++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/open_food_network/reports/rule.rb b/lib/open_food_network/reports/rule.rb index 676af48ffc..e2b0b0e255 100644 --- a/lib/open_food_network/reports/rule.rb +++ b/lib/open_food_network/reports/rule.rb @@ -1,3 +1,5 @@ +require 'open_food_network/reports/row' + module OpenFoodNetwork::Reports class Rule attr_reader :next @@ -10,13 +12,20 @@ module OpenFoodNetwork::Reports @sort = block end + def summary_row(&block) + @summary_row = Row.new + @summary_row.instance_eval(&block) + end + def organise(&block) @next = Rule.new @next.instance_eval &block end def to_h - {group_by: @group, sort_by: @sort} + h = {group_by: @group, sort_by: @sort} + h.merge!({summary_columns: @summary_row.to_a}) if @summary_row + h end end end diff --git a/spec/lib/open_food_network/reports/report_spec.rb b/spec/lib/open_food_network/reports/report_spec.rb index 72b64dbc28..7bbbccddb0 100644 --- a/spec/lib/open_food_network/reports/report_spec.rb +++ b/spec/lib/open_food_network/reports/report_spec.rb @@ -18,6 +18,11 @@ module OpenFoodNetwork::Reports organise do group { |o| o[:three] } sort { |o| o[:four] } + + summary_row do + column { |o| o[:one] } + column { |o| o[:four] } + end end end end @@ -43,6 +48,7 @@ module OpenFoodNetwork::Reports 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 @@ -53,6 +59,11 @@ module OpenFoodNetwork::Reports 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 end end diff --git a/spec/lib/open_food_network/reports/rule_spec.rb b/spec/lib/open_food_network/reports/rule_spec.rb index 16a7e49d0d..0e7a1d979b 100644 --- a/spec/lib/open_food_network/reports/rule_spec.rb +++ b/spec/lib/open_food_network/reports/rule_spec.rb @@ -19,5 +19,18 @@ module OpenFoodNetwork::Reports 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 From 142e1d6d9a17a6834c5ebe6cec1f15428dd2822c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 10:46:20 +1000 Subject: [PATCH 14/38] Report outputs rules as array for use by OrderGrouper --- lib/open_food_network/reports/report.rb | 13 ++++++++ .../open_food_network/reports/report_spec.rb | 32 +++++++++++++------ 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/lib/open_food_network/reports/report.rb b/lib/open_food_network/reports/report.rb index ad7c09ae7c..b23f77eeeb 100644 --- a/lib/open_food_network/reports/report.rb +++ b/lib/open_food_network/reports/report.rb @@ -12,6 +12,19 @@ module OpenFoodNetwork::Reports @@columns.to_a end + def rules + # Flatten linked list and return as hashes + rules = [] + + rule = @@rules_head + while rule + rules << rule + rule = rule.next + end + + rules.map &:to_h + end + # -- DSL def self.header(*columns) diff --git a/spec/lib/open_food_network/reports/report_spec.rb b/spec/lib/open_food_network/reports/report_spec.rb index 7bbbccddb0..e56a283332 100644 --- a/spec/lib/open_food_network/reports/report_spec.rb +++ b/spec/lib/open_food_network/reports/report_spec.rb @@ -1,27 +1,32 @@ 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 { |o| o[:one] } - column { |o| o[:two] } - column { |o| o[:three] } - column { |o| o[:four] } + column &P1 + column &P2 + column &P3 + column &P4 end organise do - group { |o| o[:one] } - sort { |o| o[:two] } + group &P1 + sort &P2 organise do - group { |o| o[:three] } - sort { |o| o[:four] } + group &P3 + sort &P4 summary_row do - column { |o| o[:one] } - column { |o| o[:four] } + column &P1 + column &P4 end end end @@ -65,5 +70,12 @@ module OpenFoodNetwork::Reports 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 From cc9d0defca12543f52fa09e286c732d42e08969c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 11:04:36 +1000 Subject: [PATCH 15/38] Use class attributes instead of class variables to avoid leakage between subclasses --- lib/open_food_network/reports/report.rb | 18 ++++++++++-------- .../open_food_network/reports/report_spec.rb | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/open_food_network/reports/report.rb b/lib/open_food_network/reports/report.rb index b23f77eeeb..7ddd9ebeeb 100644 --- a/lib/open_food_network/reports/report.rb +++ b/lib/open_food_network/reports/report.rb @@ -3,20 +3,22 @@ require 'open_food_network/reports/rule' module OpenFoodNetwork::Reports class Report + class_attribute :_header, :_columns, :_rules_head + # -- API def header - @@header + self._header end def columns - @@columns.to_a + self._columns.to_a end def rules # Flatten linked list and return as hashes rules = [] - rule = @@rules_head + rule = self._rules_head while rule rules << rule rule = rule.next @@ -28,17 +30,17 @@ module OpenFoodNetwork::Reports # -- DSL def self.header(*columns) - @@header = columns + self._header = columns end def self.columns(&block) - @@columns = Row.new - @@columns.instance_eval(&block) + self._columns = Row.new + self._columns.instance_eval(&block) end def self.organise(&block) - @@rules_head = Rule.new - @@rules_head.instance_eval(&block) + self._rules_head = Rule.new + self._rules_head.instance_eval(&block) end end end diff --git a/spec/lib/open_food_network/reports/report_spec.rb b/spec/lib/open_food_network/reports/report_spec.rb index e56a283332..71d5ba28fc 100644 --- a/spec/lib/open_food_network/reports/report_spec.rb +++ b/spec/lib/open_food_network/reports/report_spec.rb @@ -34,7 +34,7 @@ module OpenFoodNetwork::Reports describe Report do let(:report) { TestReport.new } - let(:rules_head) { TestReport.class_variable_get(:@@rules_head) } + let(:rules_head) { TestReport._rules_head } let(:data) { {one: 1, two: 2, three: 3, four: 4} } it "returns the header" do From a90666467aec5c24b215af19d593b5090a0a74cf Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 11:45:19 +1000 Subject: [PATCH 16/38] Add helper support to Reports DSL --- Gemfile | 1 + Gemfile.lock | 2 ++ lib/open_food_network/reports/report.rb | 4 ++-- lib/open_food_network/reports/row.rb | 3 ++- lib/open_food_network/reports/rule.rb | 5 +++-- .../open_food_network/reports/report_spec.rb | 19 +++++++++++++++++++ 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index 6d9822be68..ad5fba4cd9 100644 --- a/Gemfile +++ b/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' diff --git a/Gemfile.lock b/Gemfile.lock index 1c3fb92a5d..c1c5fd6aae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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) diff --git a/lib/open_food_network/reports/report.rb b/lib/open_food_network/reports/report.rb index 7ddd9ebeeb..d575fcd014 100644 --- a/lib/open_food_network/reports/report.rb +++ b/lib/open_food_network/reports/report.rb @@ -35,12 +35,12 @@ module OpenFoodNetwork::Reports def self.columns(&block) self._columns = Row.new - self._columns.instance_eval(&block) + Blockenspiel.invoke block, self._columns end def self.organise(&block) self._rules_head = Rule.new - self._rules_head.instance_eval(&block) + Blockenspiel.invoke block, self._rules_head end end end diff --git a/lib/open_food_network/reports/row.rb b/lib/open_food_network/reports/row.rb index 141564de94..01c8dd192e 100644 --- a/lib/open_food_network/reports/row.rb +++ b/lib/open_food_network/reports/row.rb @@ -1,10 +1,11 @@ module OpenFoodNetwork::Reports class Row + include Blockenspiel::DSL + def initialize @columns = [] end - def column(&block) @columns << block end diff --git a/lib/open_food_network/reports/rule.rb b/lib/open_food_network/reports/rule.rb index e2b0b0e255..e2f94f32a2 100644 --- a/lib/open_food_network/reports/rule.rb +++ b/lib/open_food_network/reports/rule.rb @@ -2,6 +2,7 @@ require 'open_food_network/reports/row' module OpenFoodNetwork::Reports class Rule + include Blockenspiel::DSL attr_reader :next def group(&block) @@ -14,12 +15,12 @@ module OpenFoodNetwork::Reports def summary_row(&block) @summary_row = Row.new - @summary_row.instance_eval(&block) + Blockenspiel.invoke block, @summary_row end def organise(&block) @next = Rule.new - @next.instance_eval &block + Blockenspiel.invoke block, @next end def to_h diff --git a/spec/lib/open_food_network/reports/report_spec.rb b/spec/lib/open_food_network/reports/report_spec.rb index 71d5ba28fc..0421320119 100644 --- a/spec/lib/open_food_network/reports/report_spec.rb +++ b/spec/lib/open_food_network/reports/report_spec.rb @@ -32,8 +32,23 @@ module OpenFoodNetwork::Reports 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} } @@ -48,6 +63,10 @@ module OpenFoodNetwork::Reports 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] } From dcae58467339ebf03f29ccb297c319b730d67735 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 12:02:24 +1000 Subject: [PATCH 17/38] Extract bulk coop supplier report into Report DSL --- lib/open_food_network/bulk_coop_report.rb | 33 ++----- .../reports/bulk_coop_supplier_report.rb | 98 +++++++++++++++++++ 2 files changed, 106 insertions(+), 25 deletions(-) create mode 100644 lib/open_food_network/reports/bulk_coop_supplier_report.rb diff --git a/lib/open_food_network/bulk_coop_report.rb b/lib/open_food_network/bulk_coop_report.rb index 1dc3be5a0e..2c1c4bce9e 100644 --- a/lib/open_food_network/bulk_coop_report.rb +++ b/lib/open_food_network/bulk_coop_report.rb @@ -1,15 +1,19 @@ +require 'open_food_network/reports/bulk_coop_supplier_report' + module OpenFoodNetwork class BulkCoopReport attr_reader :params def initialize(user, params = {}) @params = params @user = user + + @supplier_report = OpenFoodNetwork::Reports::BulkCoopSupplierReport.new end def header case params[:report_type] when "bulk_coop_supplier_report" - ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"] + @supplier_report.header when "bulk_coop_allocation" ["Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Allocated", "Remainder"] when "bulk_coop_packing_sheets" @@ -33,21 +37,8 @@ module OpenFoodNetwork def rules case params[:report_type] when "bulk_coop_supplier_report" - [ { 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 } } ] + @supplier_report.rules + when "bulk_coop_allocation" [ { group_by: proc { |li| li.variant.product }, sort_by: proc { |product| product.name }, @@ -96,15 +87,7 @@ module OpenFoodNetwork def columns case params[:report_type] when "bulk_coop_supplier_report" - [ 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| "" } ] + @supplier_report.columns when "bulk_coop_allocation" [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname }, proc { |lis| lis.first.variant.product.name }, diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb new file mode 100644 index 0000000000..078037e4b7 --- /dev/null +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -0,0 +1,98 @@ +require 'open_food_network/reports/report' + +module OpenFoodNetwork::Reports + class BulkCoopSupplierReport < Report + header "Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder" + + 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| total_weight(lis) } + column { |lis| total_max_quantity_weight(lis) } + column { |lis| units_required(lis) } + column { |lis| remainder(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| lis.first.variant.weight || 0 } + column { |lis| lis.sum(&:quantity) } + column { |lis| lis.sum { |li| li.max_quantity || 0 } } + column { |lis| '' } + column { |lis| '' } + end + + + 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 + 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_weight(lis) + lis.sum { |li| (li.quantity || 0) * (li.variant.weight || 0) } + end + + def total_max_quantity_weight(lis) + lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } + end + + def units_required(lis) + if group_buy_unit_size(lis).zero? + 0 + else + ( max_weight(lis) / group_buy_unit_size(lis) ).floor + end + end + + def remainder(lis) + max_weight(lis) - (units_required(lis) * group_buy_unit_size(lis)) + end + + def max_weight(lis) + max_weight = lis.sum do |li| + max_quantity = [li.max_quantity || 0, li.quantity || 0].max + max_quantity * (li.variant.weight || 0) + end + end + end + end +end From 5874ecbbef71546e8fa8525e63713a77666220f2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 12:02:31 +1000 Subject: [PATCH 18/38] Small tweaks --- lib/open_food_network/order_grouper.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/open_food_network/order_grouper.rb b/lib/open_food_network/order_grouper.rb index c0a0fc51ea..c24d1069b8 100644 --- a/lib/open_food_network/order_grouper.rb +++ b/lib/open_food_network/order_grouper.rb @@ -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 \ No newline at end of file +end From 8ee467d2b9d5c9911fbaf6075fa6eb565eb843d4 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 12:22:30 +1000 Subject: [PATCH 19/38] Split variant name into number and unit columns --- lib/open_food_network/option_value_namer.rb | 11 +++++++++++ .../reports/bulk_coop_supplier_report.rb | 6 ++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/open_food_network/option_value_namer.rb b/lib/open_food_network/option_value_namer.rb index 2c2383906d..6a77d9cb35 100644 --- a/lib/open_food_network/option_value_namer.rb +++ b/lib/open_food_network/option_value_namer.rb @@ -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? diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb index 078037e4b7..36c08c429f 100644 --- a/lib/open_food_network/reports/bulk_coop_supplier_report.rb +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -2,7 +2,7 @@ require 'open_food_network/reports/report' module OpenFoodNetwork::Reports class BulkCoopSupplierReport < Report - header "Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder" + header "Supplier", "Product", "Unit Size", "Variant value", "Variant unit", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder" organise do group { |li| li.variant.product.supplier } @@ -18,6 +18,7 @@ module OpenFoodNetwork::Reports column { |lis| group_buy_unit_size_f(lis) } column { |lis| "" } column { |lis| "" } + column { |lis| "" } column { |lis| total_weight(lis) } column { |lis| total_max_quantity_weight(lis) } column { |lis| units_required(lis) } @@ -35,7 +36,8 @@ module OpenFoodNetwork::Reports 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| lis.sum(&:quantity) } column { |lis| lis.sum { |li| li.max_quantity || 0 } } From b0dd76518155d0fac92d3f950c585b8af96bf711 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 13:44:23 +1000 Subject: [PATCH 20/38] Add a blank row after summary row in all reports --- lib/open_food_network/order_grouper.rb | 7 ++++--- spec/features/admin/reports_spec.rb | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/open_food_network/order_grouper.rb b/lib/open_food_network/order_grouper.rb index c24d1069b8..9a556e689f 100644 --- a/lib/open_food_network/order_grouper.rb +++ b/lib/open_food_network/order_grouper.rb @@ -31,10 +31,11 @@ module OpenFoodNetwork rows = [] unless is_leaf_node(groups) groups.each do |key, group| - unless key == :summary_row - build_table(group).each { |g| rows << g } - else + if key == :summary_row rows << group[:columns].map { |cols| cols.call(group[:items]) } + rows << Array.new(group[:columns].count, '') + else + build_table(group).each { |g| rows << g } end end else diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 503a52762e..f5cd9c1afa 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -124,7 +124,7 @@ feature %q{ table.sort.should == [ ["Hub", "Code", "First Name", "Last Name", "Supplier", "Product", "Variant", "Quantity", "TempControlled?"] ].sort - all('table#listing_orders tbody tr').count.should == 5 # Totals row per order + all('table#listing_orders tbody tr').count.should == 7 # Totals row per order end scenario "Pack By Supplier" do @@ -139,7 +139,7 @@ feature %q{ table.sort.should == [ ["Hub", "Supplier", "Code", "First Name", "Last Name", "Product", "Variant", "Quantity", "TempControlled?"] ].sort - all('table#listing_orders tbody tr').count.should == 4 # Totals row per supplier + all('table#listing_orders tbody tr').count.should == 5 # Totals row per supplier end end @@ -266,7 +266,7 @@ feature %q{ click_button 'Search' # Then I should see the rows for the first order but not the second - all('table#listing_orders tbody tr').count.should == 2 # Two rows per order + all('table#listing_orders tbody tr').count.should == 3 # Three rows per order end end From e8f7ab5425304ad6d29b165c2d55372ac29d9375 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 13:44:47 +1000 Subject: [PATCH 21/38] Do bulk coop report calculations using unit values rather than variant weight field --- .../reports/bulk_coop_supplier_report.rb | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb index 36c08c429f..0f24ebdc0a 100644 --- a/lib/open_food_network/reports/bulk_coop_supplier_report.rb +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -19,8 +19,8 @@ module OpenFoodNetwork::Reports column { |lis| "" } column { |lis| "" } column { |lis| "" } - column { |lis| total_weight(lis) } - column { |lis| total_max_quantity_weight(lis) } + column { |lis| total_amount(lis) } + column { |lis| total_max_quantity_amount(lis) } column { |lis| units_required(lis) } column { |lis| remainder(lis) } end @@ -69,30 +69,30 @@ module OpenFoodNetwork::Reports end end - def total_weight(lis) - lis.sum { |li| (li.quantity || 0) * (li.variant.weight || 0) } + def total_amount(lis) + lis.sum { |li| (li.quantity || 0) * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) } end - def total_max_quantity_weight(lis) - lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } + def total_max_quantity_amount(lis) + lis.sum { |li| (li.max_quantity || 0) * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) } end def units_required(lis) if group_buy_unit_size(lis).zero? 0 else - ( max_weight(lis) / group_buy_unit_size(lis) ).floor + ( max_quantity_amount(lis) / group_buy_unit_size(lis) ).floor end end def remainder(lis) - max_weight(lis) - (units_required(lis) * group_buy_unit_size(lis)) + max_quantity_amount(lis) - (units_required(lis) * group_buy_unit_size(lis)) end - def max_weight(lis) - max_weight = lis.sum do |li| + def max_quantity_amount(lis) + max_quantity_amount = lis.sum do |li| max_quantity = [li.max_quantity || 0, li.quantity || 0].max - max_quantity * (li.variant.weight || 0) + max_quantity * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) end end end From d68cfbff18555c30d1c943303decc510d6e60ded Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 13:58:47 +1000 Subject: [PATCH 22/38] Use scaled value for group_buy_unit_size --- lib/open_food_network/reports/bulk_coop_supplier_report.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb index 0f24ebdc0a..237f753bdc 100644 --- a/lib/open_food_network/reports/bulk_coop_supplier_report.rb +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -58,7 +58,8 @@ module OpenFoodNetwork::Reports end def group_buy_unit_size(lis) - lis.first.variant.product.group_buy_unit_size || 0.0 + (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) From 8665b35f1d8e0da8d248c70292301db31f1350d2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 14:16:40 +1000 Subject: [PATCH 23/38] Units required shows amount to fulfil order, not complete units required. Remainder works in opposite direction to suit. --- lib/open_food_network/reports/bulk_coop_supplier_report.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb index 237f753bdc..13d4c721af 100644 --- a/lib/open_food_network/reports/bulk_coop_supplier_report.rb +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -82,12 +82,12 @@ module OpenFoodNetwork::Reports if group_buy_unit_size(lis).zero? 0 else - ( max_quantity_amount(lis) / group_buy_unit_size(lis) ).floor + ( max_quantity_amount(lis) / group_buy_unit_size(lis) ).ceil end end def remainder(lis) - max_quantity_amount(lis) - (units_required(lis) * group_buy_unit_size(lis)) + (units_required(lis) * group_buy_unit_size(lis)) - max_quantity_amount(lis) end def max_quantity_amount(lis) From f5c2386296e9013732cba6e35d79039c45a31e24 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 14:17:00 +1000 Subject: [PATCH 24/38] Do not show non-group buy products on bulk coop reports --- lib/open_food_network/bulk_coop_report.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/open_food_network/bulk_coop_report.rb b/lib/open_food_network/bulk_coop_report.rb index 2c1c4bce9e..6edc74a4e7 100644 --- a/lib/open_food_network/bulk_coop_report.rb +++ b/lib/open_food_network/bulk_coop_report.rb @@ -31,7 +31,9 @@ module OpenFoodNetwork def table_items orders = search.result - orders.map { |o| o.line_items.managed_by(@user) }.flatten + orders. + map { |o| o.line_items.managed_by(@user) }.flatten. + select { |li| li.product.group_buy? && li.product.group_buy_unit_size.andand > 0 } end def rules From bdd792a3ea11706b92a5fa481d5c1c8b81f0e800 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 14:49:04 +1000 Subject: [PATCH 25/38] Extract helpers to new superclass --- .../reports/bulk_coop_report.rb | 59 +++++++++++++++++++ .../reports/bulk_coop_supplier_report.rb | 57 +----------------- 2 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 lib/open_food_network/reports/bulk_coop_report.rb diff --git a/lib/open_food_network/reports/bulk_coop_report.rb b/lib/open_food_network/reports/bulk_coop_report.rb new file mode 100644 index 0000000000..012367c9b5 --- /dev/null +++ b/lib/open_food_network/reports/bulk_coop_report.rb @@ -0,0 +1,59 @@ +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) * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) } + end + + def total_max_quantity_amount(lis) + lis.sum { |li| (li.max_quantity || 0) * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) } + end + + def units_required(lis) + if group_buy_unit_size(lis).zero? + 0 + else + ( max_quantity_amount(lis) / group_buy_unit_size(lis) ).ceil + end + end + + def remainder(lis) + (units_required(lis) * group_buy_unit_size(lis)) - max_quantity_amount(lis) + end + + def max_quantity_amount(lis) + max_quantity_amount = lis.sum do |li| + max_quantity = [li.max_quantity || 0, li.quantity || 0].max + max_quantity * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) + end + end + end + end +end diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb index 13d4c721af..5ef2fa624b 100644 --- a/lib/open_food_network/reports/bulk_coop_supplier_report.rb +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -1,7 +1,7 @@ -require 'open_food_network/reports/report' +require 'open_food_network/reports/bulk_coop_report' module OpenFoodNetwork::Reports - class BulkCoopSupplierReport < Report + class BulkCoopSupplierReport < BulkCoopReport header "Supplier", "Product", "Unit Size", "Variant value", "Variant unit", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder" organise do @@ -44,58 +44,5 @@ module OpenFoodNetwork::Reports column { |lis| '' } column { |lis| '' } end - - - 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) * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) } - end - - def total_max_quantity_amount(lis) - lis.sum { |li| (li.max_quantity || 0) * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) } - end - - def units_required(lis) - if group_buy_unit_size(lis).zero? - 0 - else - ( max_quantity_amount(lis) / group_buy_unit_size(lis) ).ceil - end - end - - def remainder(lis) - (units_required(lis) * group_buy_unit_size(lis)) - max_quantity_amount(lis) - end - - def max_quantity_amount(lis) - max_quantity_amount = lis.sum do |li| - max_quantity = [li.max_quantity || 0, li.quantity || 0].max - max_quantity * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) - end - end - end end end From 1a89c3caf4c97b0430eb9698d9d6b8ce85369c9a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 15:07:39 +1000 Subject: [PATCH 26/38] Translate bulk coop allocation report to reports DSL --- .../reports/bulk_coop_allocation_report.rb | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 lib/open_food_network/reports/bulk_coop_allocation_report.rb diff --git a/lib/open_food_network/reports/bulk_coop_allocation_report.rb b/lib/open_food_network/reports/bulk_coop_allocation_report.rb new file mode 100644 index 0000000000..44508627aa --- /dev/null +++ b/lib/open_food_network/reports/bulk_coop_allocation_report.rb @@ -0,0 +1,46 @@ +require 'open_food_network/reports/bulk_coop_report' + +module OpenFoodNetwork::Reports + class BulkCoopAllocationReport < BulkCoopReport + header "Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Allocated", "Remainder" + + 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| total_amount(lis) } + column { |lis| total_max_quantity_amount(lis) } + column { |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) } + column { |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) ) } + 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| lis.first.variant.weight || 0 } + column { |lis| lis.sum { |li| li.quantity } } + column { |lis| lis.sum { |li| li.max_quantity || 0 } } + column { |lis| "" } + column { |lis| "" } + end + end +end From ae968cd0eba19e1cd14c47149b83bd5e4dc62cee Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 16:21:37 +1000 Subject: [PATCH 27/38] Simple refactoring of original total_allocated code --- .../reports/bulk_coop_report.rb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/open_food_network/reports/bulk_coop_report.rb b/lib/open_food_network/reports/bulk_coop_report.rb index 012367c9b5..5faeb58033 100644 --- a/lib/open_food_network/reports/bulk_coop_report.rb +++ b/lib/open_food_network/reports/bulk_coop_report.rb @@ -7,6 +7,7 @@ module OpenFoodNetwork::Reports private class << self + def supplier_name(lis) lis.first.variant.product.supplier.name end @@ -44,12 +45,28 @@ module OpenFoodNetwork::Reports end end + def total_allocated(lis) + # Proposed new factoring: + # units_required * group_buy_unit_size + + num_units = if (lis.first.variant.product.group_buy_unit_size || 0).zero? + 0 + else + max_weight = lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } + group_buy_unit_size = lis.first.variant.product.group_buy_unit_size + + (max_weight / group_buy_unit_size).floor + end + + num_units * (lis.first.variant.product.group_buy_unit_size || 0) + end + def remainder(lis) (units_required(lis) * group_buy_unit_size(lis)) - max_quantity_amount(lis) end def max_quantity_amount(lis) - max_quantity_amount = lis.sum do |li| + lis.sum do |li| max_quantity = [li.max_quantity || 0, li.quantity || 0].max max_quantity * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) end From 4d71a56e38b7a110138ba49edfd3fafcf9b2c67d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 23 Jul 2015 16:40:50 +1000 Subject: [PATCH 28/38] Update available/unallocated calculations --- .../reports/bulk_coop_allocation_report.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/open_food_network/reports/bulk_coop_allocation_report.rb b/lib/open_food_network/reports/bulk_coop_allocation_report.rb index 44508627aa..2fa1e5a4e4 100644 --- a/lib/open_food_network/reports/bulk_coop_allocation_report.rb +++ b/lib/open_food_network/reports/bulk_coop_allocation_report.rb @@ -2,7 +2,7 @@ require 'open_food_network/reports/bulk_coop_report' module OpenFoodNetwork::Reports class BulkCoopAllocationReport < BulkCoopReport - header "Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Allocated", "Remainder" + header "Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Available", "Unallocated" organise do group { |li| li.variant.product } @@ -16,8 +16,8 @@ module OpenFoodNetwork::Reports column { |lis| "" } column { |lis| total_amount(lis) } column { |lis| total_max_quantity_amount(lis) } - column { |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) } - column { |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) ) } + column { |lis| total_available(lis) } + column { |lis| remainder(lis) } end organise do From 28de125b59c1ddafb2f41d560e71df1d03131a09 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 07:00:49 +1000 Subject: [PATCH 29/38] Bulk coop allocation report uses unit value rather than variant weight --- lib/open_food_network/bulk_coop_report.rb | 31 +++---------------- .../reports/bulk_coop_report.rb | 18 ++--------- 2 files changed, 8 insertions(+), 41 deletions(-) diff --git a/lib/open_food_network/bulk_coop_report.rb b/lib/open_food_network/bulk_coop_report.rb index 6edc74a4e7..0a88132299 100644 --- a/lib/open_food_network/bulk_coop_report.rb +++ b/lib/open_food_network/bulk_coop_report.rb @@ -1,4 +1,5 @@ require 'open_food_network/reports/bulk_coop_supplier_report' +require 'open_food_network/reports/bulk_coop_allocation_report' module OpenFoodNetwork class BulkCoopReport @@ -8,6 +9,7 @@ module OpenFoodNetwork @user = user @supplier_report = OpenFoodNetwork::Reports::BulkCoopSupplierReport.new + @allocation_report = OpenFoodNetwork::Reports::BulkCoopAllocationReport.new end def header @@ -15,7 +17,7 @@ module OpenFoodNetwork when "bulk_coop_supplier_report" @supplier_report.header when "bulk_coop_allocation" - ["Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Allocated", "Remainder"] + @allocation_report.header when "bulk_coop_packing_sheets" ["Customer", "Product", "Variant", "Sum Total"] when "bulk_coop_customer_payments" @@ -40,23 +42,8 @@ module OpenFoodNetwork case params[:report_type] when "bulk_coop_supplier_report" @supplier_report.rules - when "bulk_coop_allocation" - [ { 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 } } ] + @allocation_report.rules when "bulk_coop_packing_sheets" [ { group_by: proc { |li| li.variant.product }, sort_by: proc { |product| product.name } }, @@ -91,15 +78,7 @@ module OpenFoodNetwork when "bulk_coop_supplier_report" @supplier_report.columns when "bulk_coop_allocation" - [ 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| "" } ] + @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 }, diff --git a/lib/open_food_network/reports/bulk_coop_report.rb b/lib/open_food_network/reports/bulk_coop_report.rb index 5faeb58033..877d5c4280 100644 --- a/lib/open_food_network/reports/bulk_coop_report.rb +++ b/lib/open_food_network/reports/bulk_coop_report.rb @@ -45,24 +45,12 @@ module OpenFoodNetwork::Reports end end - def total_allocated(lis) - # Proposed new factoring: - # units_required * group_buy_unit_size - - num_units = if (lis.first.variant.product.group_buy_unit_size || 0).zero? - 0 - else - max_weight = lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } - group_buy_unit_size = lis.first.variant.product.group_buy_unit_size - - (max_weight / group_buy_unit_size).floor - end - - num_units * (lis.first.variant.product.group_buy_unit_size || 0) + def total_available(lis) + units_required(lis) * group_buy_unit_size(lis) end def remainder(lis) - (units_required(lis) * group_buy_unit_size(lis)) - max_quantity_amount(lis) + total_available(lis) - max_quantity_amount(lis) end def max_quantity_amount(lis) From 3abce541158356080653fba68b900512886f4250 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 07:07:49 +1000 Subject: [PATCH 30/38] Split variant name into number and unit columns --- .../reports/bulk_coop_allocation_report.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/open_food_network/reports/bulk_coop_allocation_report.rb b/lib/open_food_network/reports/bulk_coop_allocation_report.rb index 2fa1e5a4e4..b0d07be814 100644 --- a/lib/open_food_network/reports/bulk_coop_allocation_report.rb +++ b/lib/open_food_network/reports/bulk_coop_allocation_report.rb @@ -2,7 +2,7 @@ require 'open_food_network/reports/bulk_coop_report' module OpenFoodNetwork::Reports class BulkCoopAllocationReport < BulkCoopReport - header "Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Available", "Unallocated" + header "Customer", "Product", "Unit Size", "Variant value", "Variant unit", "Weight", "Sum Total", "Sum Max Total", "Total Available", "Unallocated" organise do group { |li| li.variant.product } @@ -14,6 +14,7 @@ module OpenFoodNetwork::Reports column { |lis| group_buy_unit_size_f(lis) } column { |lis| "" } column { |lis| "" } + column { |lis| "" } column { |lis| total_amount(lis) } column { |lis| total_max_quantity_amount(lis) } column { |lis| total_available(lis) } @@ -35,7 +36,8 @@ module OpenFoodNetwork::Reports 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| lis.sum { |li| li.quantity } } column { |lis| lis.sum { |li| li.max_quantity || 0 } } From 17fb4a7247cff10d7b6104a862a3ec4b5f33f255 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 07:17:20 +1000 Subject: [PATCH 31/38] DRY by extracting calculation to method --- lib/open_food_network/reports/bulk_coop_report.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/open_food_network/reports/bulk_coop_report.rb b/lib/open_food_network/reports/bulk_coop_report.rb index 877d5c4280..6f1e6dc6f9 100644 --- a/lib/open_food_network/reports/bulk_coop_report.rb +++ b/lib/open_food_network/reports/bulk_coop_report.rb @@ -30,11 +30,11 @@ module OpenFoodNetwork::Reports end def total_amount(lis) - lis.sum { |li| (li.quantity || 0) * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) } + lis.sum { |li| (li.quantity || 0) * scaled_amount(li) } end def total_max_quantity_amount(lis) - lis.sum { |li| (li.max_quantity || 0) * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) } + lis.sum { |li| (li.max_quantity || 0) * scaled_amount(li) } end def units_required(lis) @@ -56,9 +56,14 @@ module OpenFoodNetwork::Reports def max_quantity_amount(lis) lis.sum do |li| max_quantity = [li.max_quantity || 0, li.quantity || 0].max - max_quantity * (li.variant.unit_value || 0) / (li.product.variant_unit_scale || 1) + 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 From fffc35d9ed6c2a6a8e65af374aac79e840d19241 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 12:40:42 +1000 Subject: [PATCH 32/38] Revert "Add a blank row after summary row in all reports" This reverts commit b0dd76518155d0fac92d3f950c585b8af96bf711. --- lib/open_food_network/order_grouper.rb | 7 +++---- spec/features/admin/reports_spec.rb | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/open_food_network/order_grouper.rb b/lib/open_food_network/order_grouper.rb index 9a556e689f..c24d1069b8 100644 --- a/lib/open_food_network/order_grouper.rb +++ b/lib/open_food_network/order_grouper.rb @@ -31,11 +31,10 @@ module OpenFoodNetwork rows = [] unless is_leaf_node(groups) groups.each do |key, group| - if key == :summary_row - rows << group[:columns].map { |cols| cols.call(group[:items]) } - rows << Array.new(group[:columns].count, '') - else + unless key == :summary_row build_table(group).each { |g| rows << g } + else + rows << group[:columns].map { |cols| cols.call(group[:items]) } end end else diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index f5cd9c1afa..503a52762e 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -124,7 +124,7 @@ feature %q{ table.sort.should == [ ["Hub", "Code", "First Name", "Last Name", "Supplier", "Product", "Variant", "Quantity", "TempControlled?"] ].sort - all('table#listing_orders tbody tr').count.should == 7 # Totals row per order + all('table#listing_orders tbody tr').count.should == 5 # Totals row per order end scenario "Pack By Supplier" do @@ -139,7 +139,7 @@ feature %q{ table.sort.should == [ ["Hub", "Supplier", "Code", "First Name", "Last Name", "Product", "Variant", "Quantity", "TempControlled?"] ].sort - all('table#listing_orders tbody tr').count.should == 5 # Totals row per supplier + all('table#listing_orders tbody tr').count.should == 4 # Totals row per supplier end end @@ -266,7 +266,7 @@ feature %q{ click_button 'Search' # Then I should see the rows for the first order but not the second - all('table#listing_orders tbody tr').count.should == 3 # Three rows per order + all('table#listing_orders tbody tr').count.should == 2 # Two rows per order end end From f43eacb75dfba7beb4073dadb0b59e4a0b1833a7 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 12:40:50 +1000 Subject: [PATCH 33/38] Revert "Do not show non-group buy products on bulk coop reports" This reverts commit f5c2386296e9013732cba6e35d79039c45a31e24. --- lib/open_food_network/bulk_coop_report.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/open_food_network/bulk_coop_report.rb b/lib/open_food_network/bulk_coop_report.rb index 0a88132299..d6bf23f85b 100644 --- a/lib/open_food_network/bulk_coop_report.rb +++ b/lib/open_food_network/bulk_coop_report.rb @@ -33,9 +33,7 @@ module OpenFoodNetwork def table_items orders = search.result - orders. - map { |o| o.line_items.managed_by(@user) }.flatten. - select { |li| li.product.group_buy? && li.product.group_buy_unit_size.andand > 0 } + orders.map { |o| o.line_items.managed_by(@user) }.flatten end def rules From 211171aed4a6f6d91bfb99444aa506c4a9b823af Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 12:49:43 +1000 Subject: [PATCH 34/38] Rename column for clarity --- lib/open_food_network/reports/bulk_coop_supplier_report.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb index 5ef2fa624b..a83831f435 100644 --- a/lib/open_food_network/reports/bulk_coop_supplier_report.rb +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -2,7 +2,7 @@ require 'open_food_network/reports/bulk_coop_report' module OpenFoodNetwork::Reports class BulkCoopSupplierReport < BulkCoopReport - header "Supplier", "Product", "Unit Size", "Variant value", "Variant unit", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder" + header "Supplier", "Product", "Unit Size", "Variant value", "Variant unit", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Unallocated" organise do group { |li| li.variant.product.supplier } From 9ffc97f8b65eb2257b37d00e6b8df883e227f119 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 14:10:04 +1000 Subject: [PATCH 35/38] Fix controller spec for extraction of report controller logic --- .../spree/admin/reports_controller_spec.rb | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/spec/controllers/spree/admin/reports_controller_spec.rb b/spec/controllers/spree/admin/reports_controller_spec.rb index e930c1e339..b333e9b14e 100644 --- a/spec/controllers/spree/admin/reports_controller_spec.rb +++ b/spec/controllers/spree/admin/reports_controller_spec.rb @@ -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 From 54fd298e3a8b6564670310fdb38db7a6630fa605 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 17:42:00 +1000 Subject: [PATCH 36/38] Reinstate variant full name column - needed to identify variants differing only by description --- lib/open_food_network/reports/bulk_coop_allocation_report.rb | 4 +++- lib/open_food_network/reports/bulk_coop_supplier_report.rb | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/open_food_network/reports/bulk_coop_allocation_report.rb b/lib/open_food_network/reports/bulk_coop_allocation_report.rb index b0d07be814..0b7fad2793 100644 --- a/lib/open_food_network/reports/bulk_coop_allocation_report.rb +++ b/lib/open_food_network/reports/bulk_coop_allocation_report.rb @@ -2,7 +2,7 @@ require 'open_food_network/reports/bulk_coop_report' module OpenFoodNetwork::Reports class BulkCoopAllocationReport < BulkCoopReport - header "Customer", "Product", "Unit Size", "Variant value", "Variant unit", "Weight", "Sum Total", "Sum Max Total", "Total Available", "Unallocated" + header "Customer", "Product", "Unit Size", "Variant", "Variant value", "Variant unit", "Weight", "Sum Total", "Sum Max Total", "Total Available", "Unallocated" organise do group { |li| li.variant.product } @@ -15,6 +15,7 @@ module OpenFoodNetwork::Reports column { |lis| "" } column { |lis| "" } column { |lis| "" } + column { |lis| "" } column { |lis| total_amount(lis) } column { |lis| total_max_quantity_amount(lis) } column { |lis| total_available(lis) } @@ -36,6 +37,7 @@ module OpenFoodNetwork::Reports 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 } diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb index a83831f435..168727a9ba 100644 --- a/lib/open_food_network/reports/bulk_coop_supplier_report.rb +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -2,7 +2,7 @@ require 'open_food_network/reports/bulk_coop_report' module OpenFoodNetwork::Reports class BulkCoopSupplierReport < BulkCoopReport - header "Supplier", "Product", "Unit Size", "Variant value", "Variant unit", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Unallocated" + header "Supplier", "Product", "Unit Size", "Variant", "Variant value", "Variant unit", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Unallocated" organise do group { |li| li.variant.product.supplier } @@ -19,6 +19,7 @@ module OpenFoodNetwork::Reports column { |lis| "" } column { |lis| "" } column { |lis| "" } + column { |lis| "" } column { |lis| total_amount(lis) } column { |lis| total_max_quantity_amount(lis) } column { |lis| units_required(lis) } @@ -36,6 +37,7 @@ module OpenFoodNetwork::Reports 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 } From 21a968120506ef4427125cd60c2def50d3bda1d5 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 17:42:23 +1000 Subject: [PATCH 37/38] Do not show negative remainder when there are no purchases --- lib/open_food_network/reports/bulk_coop_report.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/open_food_network/reports/bulk_coop_report.rb b/lib/open_food_network/reports/bulk_coop_report.rb index 6f1e6dc6f9..e0d2ed9806 100644 --- a/lib/open_food_network/reports/bulk_coop_report.rb +++ b/lib/open_food_network/reports/bulk_coop_report.rb @@ -50,7 +50,8 @@ module OpenFoodNetwork::Reports end def remainder(lis) - total_available(lis) - max_quantity_amount(lis) + remainder = total_available(lis) - max_quantity_amount(lis) + remainder >= 0 ? remainder : '' end def max_quantity_amount(lis) From 917c17af59bbf68c1b5deb7df4a932066a20878b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 24 Jul 2015 18:31:49 +1000 Subject: [PATCH 38/38] Clarify quantities used for calculations, show excess available via max_quantity --- .../reports/bulk_coop_allocation_report.rb | 8 ++++---- lib/open_food_network/reports/bulk_coop_report.rb | 12 ++++++------ .../reports/bulk_coop_supplier_report.rb | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/open_food_network/reports/bulk_coop_allocation_report.rb b/lib/open_food_network/reports/bulk_coop_allocation_report.rb index 0b7fad2793..52efbc677c 100644 --- a/lib/open_food_network/reports/bulk_coop_allocation_report.rb +++ b/lib/open_food_network/reports/bulk_coop_allocation_report.rb @@ -2,7 +2,7 @@ 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", "Sum Max Total", "Total Available", "Unallocated" + 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 } @@ -17,9 +17,9 @@ module OpenFoodNetwork::Reports column { |lis| "" } column { |lis| "" } column { |lis| total_amount(lis) } - column { |lis| total_max_quantity_amount(lis) } column { |lis| total_available(lis) } column { |lis| remainder(lis) } + column { |lis| max_quantity_excess(lis) } end organise do @@ -41,8 +41,8 @@ module OpenFoodNetwork::Reports 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| lis.sum { |li| li.quantity } } - column { |lis| lis.sum { |li| li.max_quantity || 0 } } + column { |lis| total_amount(lis) } + column { |lis| "" } column { |lis| "" } column { |lis| "" } end diff --git a/lib/open_food_network/reports/bulk_coop_report.rb b/lib/open_food_network/reports/bulk_coop_report.rb index e0d2ed9806..5c0916585d 100644 --- a/lib/open_food_network/reports/bulk_coop_report.rb +++ b/lib/open_food_network/reports/bulk_coop_report.rb @@ -33,15 +33,11 @@ module OpenFoodNetwork::Reports lis.sum { |li| (li.quantity || 0) * scaled_amount(li) } end - def total_max_quantity_amount(lis) - lis.sum { |li| (li.max_quantity || 0) * scaled_amount(li) } - end - def units_required(lis) if group_buy_unit_size(lis).zero? 0 else - ( max_quantity_amount(lis) / group_buy_unit_size(lis) ).ceil + ( total_amount(lis) / group_buy_unit_size(lis) ).ceil end end @@ -50,10 +46,14 @@ module OpenFoodNetwork::Reports end def remainder(lis) - remainder = total_available(lis) - max_quantity_amount(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 diff --git a/lib/open_food_network/reports/bulk_coop_supplier_report.rb b/lib/open_food_network/reports/bulk_coop_supplier_report.rb index 168727a9ba..b40557ea1a 100644 --- a/lib/open_food_network/reports/bulk_coop_supplier_report.rb +++ b/lib/open_food_network/reports/bulk_coop_supplier_report.rb @@ -2,7 +2,7 @@ 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", "Sum Max Total", "Units Required", "Unallocated" + 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 } @@ -21,9 +21,9 @@ module OpenFoodNetwork::Reports column { |lis| "" } column { |lis| "" } column { |lis| total_amount(lis) } - column { |lis| total_max_quantity_amount(lis) } column { |lis| units_required(lis) } column { |lis| remainder(lis) } + column { |lis| max_quantity_excess(lis) } end organise do @@ -41,8 +41,8 @@ module OpenFoodNetwork::Reports 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| lis.sum(&:quantity) } - column { |lis| lis.sum { |li| li.max_quantity || 0 } } + column { |lis| total_amount(lis) } + column { |lis| '' } column { |lis| '' } column { |lis| '' } end