From c168dec14b5716060e06f0c5f7efcaefe1a494a7 Mon Sep 17 00:00:00 2001 From: Lynne Davis Date: Tue, 19 May 2015 19:00:23 +0100 Subject: [PATCH] Adding packing reports as required by the UK contingent --- .../admin/reports_controller_decorator.rb | 34 +++++ .../reports/_packing_description.html.haml | 4 + .../spree/admin/reports/packing.html.haml | 42 ++++++ lib/open_food_network/packing_report.rb | 121 ++++++++++++++++++ spec/features/admin/reports_spec.rb | 60 +++++++++ 5 files changed, 261 insertions(+) create mode 100644 app/views/spree/admin/reports/_packing_description.html.haml create mode 100644 app/views/spree/admin/reports/packing.html.haml create mode 100644 lib/open_food_network/packing_report.rb diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 81975bb250..76e4177ca1 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -75,6 +75,40 @@ Spree::Admin::ReportsController.class_eval do render_report(@report.header, @report.table, params[:csv], "order_cycle_management_#{timestamp}.csv") 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 form options + my_distributors = Enterprise.is_distributor.managed_by(spree_current_user) + my_suppliers = Enterprise.is_primary_producer.managed_by(spree_current_user) + + # My distributors and any distributors distributing products I supply + @distributors = my_distributors | Enterprise.with_distributed_products_outer.merge(Spree::Product.in_any_supplier(my_suppliers)) + # My suppliers and any suppliers supplying products I distribute + @suppliers = my_suppliers | my_distributors.map { |d| Spree::Product.in_distributor(d) }.flatten.map(&:supplier).uniq + @order_cycles = OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC') + @report_types = REPORT_TYPES[:packing] + @report_type = params[:report_type] + + # -- Build Report with Order Grouper + @report = OpenFoodNetwork::PackingReport.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(@report.header, @table, params[:csv], "packing_#{timestamp}.csv") + end + def orders_and_distributors params[:q] ||= {} diff --git a/app/views/spree/admin/reports/_packing_description.html.haml b/app/views/spree/admin/reports/_packing_description.html.haml new file mode 100644 index 0000000000..3d7855cb89 --- /dev/null +++ b/app/views/spree/admin/reports/_packing_description.html.haml @@ -0,0 +1,4 @@ +%ul{style: "margin-left: 12pt"} + - report_types.each do |report_type| + %li + = link_to report_type[0], "#{packing_admin_reports_url}?report_type=#{report_type[1]}" diff --git a/app/views/spree/admin/reports/packing.html.haml b/app/views/spree/admin/reports/packing.html.haml new file mode 100644 index 0000000000..2aa8d8c8b9 --- /dev/null +++ b/app/views/spree/admin/reports/packing.html.haml @@ -0,0 +1,42 @@ += form_for @report.search, :url => spree.packing_admin_reports_path do |f| + = render 'date_range_form', f: f + + .row + .alpha.two.columns= label_tag nil, "Hubs: " + .omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Producers: " + .omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Order Cycles: " + .omega.fourteen.columns + = f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params[:q][:order_cycle_id_in]}, {class: "select2 fullwidth", multiple: true}) + + .row + .alpha.two.columns= label_tag nil, "Report Type: " + .omega.fourteen.columns= select_tag(:report_type, options_for_select(@report_types, @report_type)) + + .row + = check_box_tag :csv + = label_tag :csv, "Download as csv" + + .row + = button t(:search) + +%br +%br +%table#listing_orders.index + %thead + %tr{'data-hook' => "orders_header"} + - @report.header.each do |heading| + %th=heading + %tbody + - @table.each do |row| + %tr + - row.each do |column| + %td= column + - if @table.empty? + %tr + %td{:colspan => "2"}= t(:none) diff --git a/lib/open_food_network/packing_report.rb b/lib/open_food_network/packing_report.rb new file mode 100644 index 0000000000..5d42add700 --- /dev/null +++ b/lib/open_food_network/packing_report.rb @@ -0,0 +1,121 @@ +module OpenFoodNetwork + class PackingReport + attr_reader :params + def initialize(user, params = {}) + @params = params + @user = user + end + + def header + if is_by_customer? + ["Hub", "Code", "First Name", "Last Name", "Supplier", "Product", "Variant", "Quantity", "TempControlled?"] + else + ["Hub", "Supplier", "Code", "First Name", "Last Name", "Product", "Variant", "Quantity", "TempControlled?"] + end + end + + def search + Spree::Order.complete.not_state(:canceled).managed_by(@user).search(params[:q]) + end + + def table_items + orders = search.result + @line_items = orders.map do |o| + lis = o.line_items.managed_by(@user) + lis = lis.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present? + lis + end.flatten + end + + def rules + if is_by_customer? +# customer_rows orders +# table_items = @line_items + + [ + { 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 }, + summary_columns: [ proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "TOTAL ITEMS" }, + proc { |line_items| "" }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + 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 }, + sort_by: proc { |variant| variant.product.name } } ] + else +# supplier_rows orders +# table_items = supplier_rows orders +# + [ { group_by: proc { |line_item| line_item.order.distributor }, + sort_by: proc { |distributor| distributor.name } }, + { group_by: proc { |line_item| line_item.variant.product.supplier }, + sort_by: proc { |supplier| supplier.name }, + summary_columns: [ proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "" }, + proc { |line_items| "TOTAL ITEMS" }, + proc { |line_items| "" }, + proc { |line_items| line_items.sum { |li| li.quantity } }, + proc { |line_items| "" } ] }, + { group_by: proc { |line_item| line_item.variant }, + sort_by: proc { |variant| variant.product.name } } ] + end + end + + def columns + if is_by_customer? + [ proc { |line_items| line_items.first.order.distributor.name }, + proc { |line_items| customer_code(line_items.first.order.email) }, + proc { |line_items| line_items.first.order.bill_address.firstname }, + proc { |line_items| line_items.first.order.bill_address.lastname }, + 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| is_temperature_controlled?(line_items.first) } + ] + else + [ + proc { |line_items| line_items.first.order.distributor.name }, + proc { |line_items| line_items.first.variant.product.supplier.name }, + proc { |line_items| customer_code(line_items.first.order.email) }, + proc { |line_items| line_items.first.order.bill_address.firstname }, + proc { |line_items| line_items.first.order.bill_address.lastname }, + 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| is_temperature_controlled?(line_items.first) } + ] + end + end + + private + + def is_temperature_controlled?(line_item) + if line_item.product.shipping_category.andand.temperature_controlled + "Yes" + else + "No" + end + end + + def is_by_customer? + params[:report_type] == "pack_by_customer" + end + + def customer_code(email) + customer = Customer.where(email: email).first + customer.nil? ? "" : customer.code + end + end +end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 4f820bb004..503a52762e 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -84,6 +84,66 @@ feature %q{ end end + describe "Packing reports" do + before do + login_to_admin_section + click_link "Reports" + end + + let(:bill_address1) { create(:address, lastname: "Aman") } + let(:bill_address2) { create(:address, lastname: "Bman") } + let(:distributor_address) { create(:address, :address1 => "distributor address", :city => 'The Shire', :zipcode => "1234") } + let(:distributor) { create(:distributor_enterprise, :address => distributor_address) } + let(:order1) { create(:order, distributor: distributor, bill_address: bill_address1) } + let(:order2) { create(:order, distributor: distributor, bill_address: bill_address2) } + let(:supplier) { create(:supplier_enterprise, name: "Supplier") } + let(:product_1) { create(:simple_product, name: "Product 1", supplier: supplier ) } + let(:variant_1) { create(:variant, product: product_1, unit_description: "Big") } + let(:variant_2) { create(:variant, product: product_1, unit_description: "Small") } + let(:product_2) { create(:simple_product, name: "Product 2", supplier: supplier) } + + before do + Timecop.travel(Time.zone.local(2013, 4, 25, 14, 0, 0)) { order1.finalize! } + Timecop.travel(Time.zone.local(2013, 4, 25, 15, 0, 0)) { order2.finalize! } + + create(:line_item, variant: variant_1, quantity: 1, order: order1) + create(:line_item, variant: variant_2, quantity: 3, order: order1) + create(:line_item, variant: product_2.master, quantity: 3, order: order2) + + end + + scenario "Pack By Customer" do + click_link "Pack By Customer" + fill_in 'q_completed_at_gt', with: '2013-04-25 13:00:00' + fill_in 'q_completed_at_lt', with: '2013-04-25 16:00:00' + #select 'Pack By Customer', from: 'report_type' + click_button 'Search' + + rows = find("table#listing_orders.index").all("thead tr") + table = rows.map { |r| r.all("th").map { |c| c.text.strip } } + 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 + end + + scenario "Pack By Supplier" do + click_link "Pack By Supplier" + fill_in 'q_completed_at_gt', with: '2013-04-25 13:00:00' + fill_in 'q_completed_at_lt', with: '2013-04-25 16:00:00' + #select 'Pack By Customer', from: 'report_type' + click_button 'Search' + + rows = find("table#listing_orders").all("thead tr") + table = rows.map { |r| r.all("th").map { |c| c.text.strip } } + 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 + end + end + + scenario "orders and distributors report" do login_to_admin_section click_link 'Reports'