Merge branch 'master' into enterprises

Conflicts:
	app/controllers/enterprises_controller.rb
	app/views/spree/checkout/_distributor.html.haml
	db/schema.rb
	spec/lib/open_food_web/group_buy_report_spec.rb
This commit is contained in:
Rohan Mitchell
2012-11-01 11:02:02 +11:00
23 changed files with 945 additions and 36 deletions

View File

@@ -23,7 +23,7 @@ GIT
GIT
remote: git://github.com/eaterprises/spree_paypal_express.git
revision: ec7fd298f117fe6c598070dc0e7a38887b9bcbc4
revision: 7dd46e6e549d82c4d603f685a7cb7fbb4af0d973
branch: 1-1-stable
specs:
spree_paypal_express (1.1.0)
@@ -320,13 +320,13 @@ GEM
libv8 (~> 3.3.10)
thor (0.16.0)
tilt (1.3.3)
treetop (1.4.11)
treetop (1.4.12)
polyglot
polyglot (>= 0.3.1)
truncate_html (0.5.5)
turn (0.8.3)
ansi
tzinfo (0.3.33)
tzinfo (0.3.34)
uglifier (1.2.4)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)

View File

@@ -10,6 +10,15 @@ $(document).ready(function() {
// Product page with master price only
$(".add-to-cart input.title:not(#quantity):not(.max_quantity)").change(products_update_price_without_variant).change();
// Product page other
$("#distributor_id").change(function() {
var distributor_html = distributors[$(this).val()];
if(!distributor_html) {
distributor_html = 'When you select a distributor for your order, their address and pickup times will be displayed here.';
}
$("#product-distributor-details .distributor-details").html(distributor_html);
});
});

View File

@@ -144,6 +144,19 @@ ul.product-listing {
}
/* Distributor details on product details page */
fieldset#product-distributor-details {
float: right;
margin-top: 0;
width: 250px;
@extend #shipping;
}
#product-variants {
float: none;
}
/* Add to cart form on product details page */
#cart-form {
.error-distributor {

View File

@@ -7,6 +7,16 @@ class EnterprisesController < BaseController
@suppliers = Enterprise.is_supplier
end
def distributors
@distributors = Enterprise.is_distributor
respond_to do |format|
format.js do
@distributor_details = Hash[@distributors.map { |d| [d.id, render_to_string(:partial => 'enterprises/distributor_details', :locals => {:distributor => d})] }]
end
end
end
def show
options = {:enterprise_id => params[:id]}
options.merge(params.reject { |k,v| k == :id })

View File

@@ -1,11 +1,15 @@
require 'csv'
require 'open_food_web/order_and_distributor_report'
require 'open_food_web/group_buy_report'
require 'open_food_web/order_grouper'
Spree::Admin::ReportsController.class_eval do
Spree::Admin::ReportsController::AVAILABLE_REPORTS.merge!({:orders_and_distributors => {:name => "Orders And Distributors", :description => "Orders with distributor details"}})
Spree::Admin::ReportsController::AVAILABLE_REPORTS.merge!({:group_buys => {:name => "Group Buys", :description => "Orders by supplier and variant"}})
Spree::Admin::ReportsController::AVAILABLE_REPORTS.merge!({:bulk_coop => {:name => "Bulk Co-Op", :description => "Reports for Bulk Co-Op orders"}})
Spree::Admin::ReportsController::AVAILABLE_REPORTS.merge!({:payments => {:name => "Payment Reports", :description => "Reports for Payments"}})
Spree::Admin::ReportsController::AVAILABLE_REPORTS.merge!({:order_cycles => {:name => "Order Cycle Reports", :description => "Reports for Order Cycles"}})
def orders_and_distributors
params[:q] = {} unless params[:q]
@@ -67,4 +71,424 @@ Spree::Admin::ReportsController.class_eval do
end
end
def bulk_coop
params[:q] = {} unless params[:q]
if params[:q][:created_at_gt].blank?
params[:q][:created_at_gt] = Time.zone.now.beginning_of_month
else
params[:q][:created_at_gt] = Time.zone.parse(params[:q][:created_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
end
if params[:q] && !params[:q][:created_at_lt].blank?
params[:q][:created_at_lt] = Time.zone.parse(params[:q][:created_at_lt]).end_of_day rescue ""
end
params[:q][:meta_sort] ||= "created_at.desc"
@search = Spree::Order.complete.search(params[:q])
orders = @search.result
line_items = orders.map { |o| o.line_items }.flatten
@distributors = Enterprise.is_distributor
@report_type = params[:report_type]
case params[:report_type]
when "bulk_coop_supplier_report"
header = ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total"]
columns = [ proc { |lis| lis.first.variant.product.supplier.name },
proc { |lis| lis.first.variant.product.name },
proc { |lis| "UNIT SIZE" },
proc { |lis| lis.first.variant.options_text },
proc { |lis| lis.first.variant.weight || 0 },
proc { |lis| lis.sum { |li| li.quantity } },
proc { |lis| lis.sum { |li| li.max_quantity || 0 } } ]
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| "UNIT SIZE" },
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 } } ] },
{ group_by: proc { |li| li.variant },
sort_by: proc { |variant| variant.options_text } } ]
when "bulk_coop_allocation"
header = ["Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max 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| "UNIT SIZE" },
proc { |lis| lis.first.variant.options_text },
proc { |lis| lis.first.variant.weight || 0 },
proc { |lis| lis.sum { |li| li.quantity } },
proc { |lis| lis.sum { |li| li.max_quantity || 0 } } ]
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.options_text } },
{ group_by: proc { |li| li.order.user },
sort_by: proc { |user| user.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.options_text },
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.options_text } },
{ group_by: proc { |li| li.order.user },
sort_by: proc { |user| user.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.created_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.user },
sort_by: proc { |user| user.to_s } },
{ group_by: proc { |li| li.order },
sort_by: proc { |order| order.created_at } } ]
else # List all line items
header = ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total"]
columns = [ proc { |lis| lis.first.variant.product.supplier.name },
proc { |lis| lis.first.variant.product.name },
proc { |lis| "UNIT SIZE" },
proc { |lis| lis.first.variant.options_text },
proc { |lis| lis.first.variant.weight || 0 },
proc { |lis| lis.sum { |li| li.quantity } },
proc { |lis| lis.sum { |li| li.max_quantity || 0 } } ]
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 } },
{ group_by: proc { |li| li.variant },
sort_by: proc { |variant| variant.options_text } } ]
end
order_grouper = OpenFoodWeb::OrderGrouper.new rules, columns
@header = header
@table = order_grouper.table(line_items)
csv_file_name = "bulk_coop.csv"
render_report(@header, @table, params[:csv], csv_file_name)
end
def payments
params[:q] = {} unless params[:q]
if params[:q][:created_at_gt].blank?
params[:q][:created_at_gt] = Time.zone.now.beginning_of_month
else
params[:q][:created_at_gt] = Time.zone.parse(params[:q][:created_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
end
if params[:q] && !params[:q][:created_at_lt].blank?
params[:q][:created_at_lt] = Time.zone.parse(params[:q][:created_at_lt]).end_of_day rescue ""
end
params[:q][:meta_sort] ||= "created_at.desc"
@search = Spree::Order.complete.search(params[:q])
orders = @search.result
payments = orders.map { |o| o.payments.select { |payment| payment.completed? } }.flatten # Only select completed payments
@distributors = Enterprise.is_distributor
@report_type = params[:report_type]
case params[:report_type]
when "payments_by_payment_type"
table_items = payments
header = ["Payment State", "Distributor", "Payment Type", "Total ($)"]
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 } } ]
when "itemised_payment_totals"
table_items = orders
header = ["Payment State", "Distributor", "Product Total ($)", "Shipping Total ($)", "Outstanding Balance ($)", "Total ($)"]
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 ($)", "Shipping Total ($)", "Total ($)", "EFT ($)", "PayPal ($)", "Outstanding Balance ($)"]
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 ($)"]
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 = OpenFoodWeb::OrderGrouper.new rules, columns
@header = header
@table = order_grouper.table(table_items)
csv_file_name = "payments.csv"
render_report(@header, @table, params[:csv], csv_file_name)
end
def order_cycles
params[:q] = {} unless params[:q]
if params[:q][:created_at_gt].blank?
params[:q][:created_at_gt] = Time.zone.now.beginning_of_month
else
params[:q][:created_at_gt] = Time.zone.parse(params[:q][:created_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
end
if params[:q] && !params[:q][:created_at_lt].blank?
params[:q][:created_at_lt] = Time.zone.parse(params[:q][:created_at_lt]).end_of_day rescue ""
end
params[:q][:meta_sort] ||= "created_at.desc"
@search = Spree::Order.complete.search(params[:q])
orders = @search.result
line_items = orders.map { |o| o.line_items }.flatten
#payments = orders.map { |o| o.payments.select { |payment| payment.completed? } }.flatten # Only select completed payments
@distributors = Enterprise.is_distributor
@report_type = params[:report_type]
case params[:report_type]
when "order_cycle_supplier_totals"
table_items = line_items
@include_blank = 'All'
header = ["Supplier", "Product", "Variant", "Amount", "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.options_text },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.first.variant.price },
proc { |line_items| line_items.sum { |li| li.quantity * li.variant.price } },
proc { |line_items| "status" },
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.options_text } } ]
when "order_cycle_supplier_totals_by_distributor"
table_items = line_items
@include_blank = 'All'
header = ["Supplier", "Product", "Variant", "To Distributor", "Amount", "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.options_text },
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.first.variant.price },
proc { |line_items| line_items.sum { |li| li.quantity * li.variant.price } },
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.options_text },
summary_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.options_text },
proc { |line_items| "TOTAL" },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.first.variant.price },
proc { |line_items| line_items.sum { |li| li.quantity * li.variant.price } },
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 = ["Distributor", "Supplier", "Product", "Variant", "Amount", "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.options_text },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.first.variant.price },
proc { |line_items| line_items.sum { |li| li.quantity * li.variant.price } },
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| line_items.first.order.distributor.name },
proc { |line_items| "TOTAL" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| line_items.sum { |li| li.quantity * li.variant.price } },
proc { |line_items| "total shipping cost" },
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.options_text } } ]
when "order_cycle_customer_totals"
table_items = line_items
@include_blank = false
header = ["Customer", "Email", "Phone", "Product", "Variant", "Amount", "Total Cost", "Paid?", "Packed?", "Shipped?"]
columns = [ 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.name },
proc { |line_items| line_items.first.variant.options_text },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.sum { |li| li.quantity * li.variant.price } },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" } ]
rules = [ { group_by: proc { |line_item| line_item.order.user },
sort_by: proc { |user| user.to_s },
summary_columns: [ 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| "TOTAL" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| line_items.sum { |li| li.quantity * li.variant.price } },
proc { |line_items| "work out whether paid or not" },
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.options_text } } ]
else
table_items = line_items
header = ["Supplier", "Product", "Variant", "Amount", "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.options_text },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.first.variant.price },
proc { |line_items| line_items.sum { |li| li.quantity * li.variant.price } },
proc { |line_items| "status" },
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.options_text } } ]
end
order_grouper = OpenFoodWeb::OrderGrouper.new rules, columns
@header = header
@table = order_grouper.table(table_items)
csv_file_name = "order_cycles.csv"
render_report(@header, @table, params[:csv], csv_file_name)
end
def render_report (header, table, create_csv, csv_file_name)
unless create_csv
render :html => table
else
csv_string = CSV.generate do |csv|
csv << header
table.each { |row| csv << row }
end
send_data csv_string, :filename => csv_file_name
end
end
end

View File

@@ -0,0 +1,9 @@
Deface::Override.new(:virtual_path => "spree/products/show",
:insert_before => "[data-hook='cart_form']",
:partial => "spree/products/distributor_details",
:name => "product_distributor_details")
Deface::Override.new(:virtual_path => "spree/products/show",
:insert_after => "[data-hook='product_show']",
:text => "<%= javascript_include_tag main_app.distributors_enterprises_path(:format => :js) %>",
:name => "product_distributor_details_js")

View File

@@ -0,0 +1,23 @@
%h2= distributor.name
%p
%strong Address:
%br/
= render 'spree/shared/address', :address => distributor.address
%p
%strong Next collection time:
%br/
= distributor.next_collection_at
%p
%strong Regular collection times:
%br/
= distributor.pickup_times
%p
%strong Contact:
%br/
= distributor.contact
%br/
= "Phone: #{distributor.phone}"
%br/
= "Email: #{distributor.email}"
%p= distributor.description
%p= link_to distributor.website, distributor.website if distributor.website

View File

@@ -0,0 +1 @@
distributors = <%= @distributor_details.to_json.html_safe %>;

View File

@@ -0,0 +1,39 @@
= form_for @search, :url => spree.bulk_coop_admin_reports_path do |f|
= label_tag nil, t(:date_range)
%br
.date-range-filter
%div{"class" => "left sub-field"}
= f.text_field :created_at_gt, :class => 'datepicker'
%br
= label_tag nil, t(:start), :class => 'sub'
%div{"class" => "right sub-field"}
= f.text_field :created_at_lt, :class => 'datepicker'
%br
= label_tag nil, t(:stop)
%br
= label_tag nil, "Distributor: "
= f.collection_select(:distributor_id_eq, @distributors, :id, :name, :include_blank => 'All')
%br
= label_tag nil, "Report Type: "
= select_tag(:report_type, options_for_select([['Bulk Co-op - Totals by Supplier',:bulk_coop_supplier_report],['Bulk Co-op - Allocation',:bulk_coop_allocation],['Bulk Co-op - Packing Sheets',:bulk_coop_packing_sheets],['Bulk Co-op - Customer Payments',:bulk_coop_customer_payments]], @report_type))
%br
%br
= check_box_tag :csv
= label_tag :csv, "Download as csv"
%br
= button t(:search)
%br
%br
%table#listing_orders.index
%thead
%tr{'data-hook' => "orders_header"}
- @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)

View File

@@ -0,0 +1,39 @@
= form_for @search, :url => spree.order_cycles_admin_reports_path do |f|
= label_tag nil, t(:date_range)
%br
.date-range-filter
%div{"class" => "left sub-field"}
= f.text_field :created_at_gt, :class => 'datepicker'
%br
= label_tag nil, t(:start), :class => 'sub'
%div{"class" => "right sub-field"}
= f.text_field :created_at_lt, :class => 'datepicker'
%br
= label_tag nil, t(:stop)
%br
= label_tag nil, "Distributor: "
= f.collection_select(:distributor_id_eq, @distributors, :id, :name, :include_blank => @include_blank)
%br
= label_tag nil, "Report Type: "
= select_tag(:report_type, options_for_select([['Order Cycle Supplier Totals',:order_cycle_supplier_totals], ['Order Cycle Supplier Totals by Distributor',:order_cycle_supplier_totals_by_distributor], ['Order Cycle Distributor Totals by Supplier',:order_cycle_distributor_totals_by_supplier], ['Order Cycle Customer Totals',:order_cycle_customer_totals]], @report_type))
%br
%br
= check_box_tag :csv
= label_tag :csv, "Download as csv"
%br
= button t(:search)
%br
%br
%table#listing_orders.index
%thead
%tr{'data-hook' => "orders_header"}
- @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)

View File

@@ -0,0 +1,39 @@
= form_for @search, :url => spree.payments_admin_reports_path do |f|
= label_tag nil, t(:date_range)
%br
.date-range-filter
%div{"class" => "left sub-field"}
= f.text_field :created_at_gt, :class => 'datepicker'
%br
= label_tag nil, t(:start), :class => 'sub'
%div{"class" => "right sub-field"}
= f.text_field :created_at_lt, :class => 'datepicker'
%br
= label_tag nil, t(:stop)
%br
= label_tag nil, "Distributor: "
= f.collection_select(:distributor_id_eq, @distributors, :id, :name, :include_blank => 'All')
%br
= label_tag nil, "Report Type: "
= select_tag(:report_type, options_for_select([['Payments By Type',:payments_by_payment_type],['Itemised Payment Totals',:itemised_payment_totals],['Payment Totals',:payment_totals]], @report_type))
%br
%br
= check_box_tag :csv
= label_tag :csv, "Download as csv"
%br
= button t(:search)
%br
%br
%table#listing_orders.index
%thead
%tr{'data-hook' => "orders_header"}
- @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)

View File

@@ -1,26 +1,4 @@
.columns.omega.six
%fieldset#shipping
%legend Distributor
%h2= @order.distributor.name
%p
%strong Address:
%br/
= render 'spree/shared/address', :address => @order.distributor.address
%p
%strong Next collection time:
%br/
= @order.distributor.next_collection_at
%p
%strong Regular collection times:
%br/
= @order.distributor.pickup_times
%p
%strong Contact:
%br/
= @order.distributor.contact
%br/
= "Phone: #{@order.distributor.phone}"
%br/
= "Email: #{@order.distributor.email}"
%p= @order.distributor.description
%p= link_to @order.distributor.website, @order.distributor.website if @order.distributor.website
= render 'enterprises/distributor_details', :distributor => @order.distributor

View File

@@ -0,0 +1,8 @@
%fieldset#product-distributor-details.columns.five.omega
%legend Distributor
.distributor-details
- order = current_order(false)
- if order.andand.distributor.present?
= render 'enterprises/distributor_details', :distributor => order.distributor
- else
When you select a distributor for your order, their address and pickup times will be displayed here.

View File

@@ -3,6 +3,7 @@ Openfoodweb::Application.routes.draw do
resources :enterprises do
get :suppliers, :on => :collection
get :distributors, :on => :collection
get :select_distributor, :on => :member
get :deselect_distributor, :on => :collection
end
@@ -21,4 +22,7 @@ end
Spree::Core::Engine.routes.prepend do
match '/admin/reports/orders_and_distributors' => 'admin/reports#orders_and_distributors', :as => "orders_and_distributors_admin_reports", :via => [:get, :post]
match '/admin/reports/group_buys' => 'admin/reports#group_buys', :as => "group_buys_admin_reports", :via => [:get, :post]
match '/admin/reports/bulk_coop' => 'admin/reports#bulk_coop', :as => "bulk_coop_admin_reports", :via => [:get, :post]
match '/admin/reports/payments' => 'admin/reports#payments', :as => "payments_admin_reports", :via => [:get, :post]
match '/admin/reports/order_cycles' => 'admin/reports#order_cycles', :as => "order_cycles_admin_reports", :via => [:get, :post]
end

View File

@@ -0,0 +1,21 @@
class ChangeGroupBuyUnitSizeFromStringToFloat < ActiveRecord::Migration
class Spree::Product < ActiveRecord::Base; end
def up
add_column :spree_products, :group_buy_unit_size_f, :float
Spree::Product.reset_column_information
Spree::Product.all.each do |product|
product.group_buy_unit_size_f = product.group_buy_unit_size.to_f
product.save!
end
remove_column :spree_products, :group_buy_unit_size
rename_column :spree_products, :group_buy_unit_size_f, :group_buy_unit_size
end
def down
change_column :spree_products, :group_buy_unit_size, :string
end
end

View File

@@ -490,7 +490,7 @@ ActiveRecord::Schema.define(:version => 20121031222403) do
t.integer "count_on_hand", :default => 0, :null => false
t.integer "supplier_id"
t.boolean "group_buy"
t.string "group_buy_unit_size"
t.float "group_buy_unit_size"
end
add_index "spree_products", ["available_on"], :name => "index_products_on_available_on"

View File

@@ -38,8 +38,8 @@ module OpenFoodWeb
end
# Sum quantities for each product (Total line)
sum_quantities = line_items_by_product.sum { |li| li.variant.weight * li.quantity }
sum_max_quantities = line_items_by_product.sum { |li| li.variant.weight * (li.max_quantity || 0) }
sum_quantities = line_items_by_product.sum { |li| (li.variant.weight || 0) * li.quantity }
sum_max_quantities = line_items_by_product.sum { |li| (li.variant.weight || 0) * (li.max_quantity || 0) }
variants_and_quantities << GroupBuyProductRow.new(product, sum_quantities, sum_max_quantities)
end
end

View File

@@ -0,0 +1,58 @@
module OpenFoodWeb
class OrderGrouper
def initialize(rules, column_constructors)
@rules = rules
@column_constructors = column_constructors
end
def build_tree(items, remaining_rules)
rules = remaining_rules.clone
unless rules.empty?
rule = rules.delete_at(0) # Remove current rule for subsequent groupings
group_and_sort(rule, rules, items)
else
items
end
end
def group_and_sort(rule, remaining_rules, items)
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|
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
branch
end
def build_table(groups)
rows = []
unless is_leaf_node(groups)
groups.each do |key, group|
unless key == :summary_row
build_table(group).each { |g| rows << g }
else
rows << group[:columns].map { |cols| cols.call(group[:items]) }
end
end
else
rows << @column_constructors.map { |column_constructor| column_constructor.call(groups) }
end
rows
end
def table(items)
tree = build_tree(items,@rules)
table = build_table(tree)
table
end
private
def is_leaf_node(node)
node.is_a? Array
end
end
end

View File

@@ -36,7 +36,7 @@ module OpenFoodWeb
@orders << order2
@supplier2 = create(:supplier_enterprise)
@variant3 = create(:variant)
@variant3 = create(:variant, :weight => nil)
@variant3.product.supplier = @supplier2
@variant3.product.save!
product_distribution = create(:product_distribution, :product => @variant3.product, :distributor => distributor, :shipping_method => create(:shipping_method))
@@ -82,12 +82,12 @@ module OpenFoodWeb
table_row_objects = subject.variants_and_quantities
variant_rows = table_row_objects.select{ |r| r.class == OpenFoodWeb::GroupBuyVariantRow }
product_rows = table_row_objects.select{ |r| r.class == OpenFoodWeb::GroupBuyProductRow }
variant_rows = table_row_objects.select { |r| r.class == OpenFoodWeb::GroupBuyVariantRow }
product_rows = table_row_objects.select { |r| r.class == OpenFoodWeb::GroupBuyProductRow }
supplier_groups = variant_rows.group_by { |r| r.variant.product.supplier }
variant_groups = variant_rows.group_by{ |r| r.variant }
product_groups = product_rows.group_by{ |r| r.product }
variant_groups = variant_rows.group_by { |r| r.variant }
product_groups = product_rows.group_by { |r| r.product }
supplier_groups.length.should == 2
variant_groups.length.should == 3

View File

@@ -0,0 +1,152 @@
require 'spec_helper'
module OpenFoodWeb
describe OrderGrouper do
before(:each) do
@items = [1, 2, 3, 4]
end
context "constructing the table" do
it "should build a tree then build a table" do
rules = [ { group_by: Proc.new { |sentence| sentence.paragraph.chapter }, sort_by: Proc.new { |chapter| chapter.name }, summary_columns: [Proc.new { |is| is.first.paragraph.chapter.name }, Proc.new { |is| "TOTAL" }, Proc.new { |is| "" }, Proc.new { |is| is.sum {|i| i.property1 } } ] },
{ group_by: Proc.new { |sentence| sentence.paragraph }, sort_by: Proc.new { |paragraph| paragraph.name } } ]
columns = [Proc.new { |is| is.first.paragraph.chapter.name }, Proc.new { |is| is.first.paragraph.name }, Proc.new { |is| is.first.name }, Proc.new { |is| is.sum {|i| i.property1 } }]
subject = OrderGrouper.new rules, columns
tree = double(:tree)
subject.should_receive(:build_tree).with(@items, rules).and_return(tree)
subject.should_receive(:build_table).with(tree)
subject.table(@items)
end
end
context "grouping items without rules" do
it "returns the original array when no rules are provided" do
rules = []
column1 = double(:col1)
column2 = double(:col2)
columns = [column1, column2]
subject = OrderGrouper.new rules, columns
rules.should_receive(:clone).and_return(rules)
subject.build_tree(@items, rules).should == @items
end
end
context "grouping items with rules" do
before(:each) do
@rule1 = double(:rule1)
rule2 = double(:rule2)
@rules = [@rule1, rule2]
@remaining_rules = [rule2]
column1 = double(:col1)
column2 = double(:col2)
@columns = [column1, column2]
end
it "builds branches by removing a rule from 'rules' and running group_and_sort" do
subject = OrderGrouper.new @rules, @columns
@rules.should_receive(:clone).and_return(@rules)
@rules.should_receive(:delete_at).with(0)
grouped_tree = double(:grouped_tree)
subject.should_receive(:group_and_sort).and_return(grouped_tree)
subject.build_tree(@items, @rules).should == grouped_tree
end
it "separates the first rule from rules before sending to group_and_sort" do
subject = OrderGrouper.new @rules, @columns
grouped_tree = double(:grouped_tree)
subject.should_receive(:group_and_sort).with(@rule1, @rules[1..-1], @items).and_return(grouped_tree)
subject.build_tree(@items, @rules).should == grouped_tree
end
it "should group, then sort, send each group to build_tree, and return a branch" do
summary_columns_object = double(:summary_columns)
@rule1.stub(:[]).with(:summary_columns) { summary_columns_object }
subject = OrderGrouper.new @rules, @columns
number_of_categories = 3
groups = double(:groups)
@items.should_receive(:group_by).and_return(groups)
sorted_groups = {}
1.upto(number_of_categories) { |i| sorted_groups[i] = double(:group, name: "Group "+ i.to_s ) }
groups.should_receive(:sort_by).and_return(sorted_groups)
group = { group1: 1, group2: 2, group3: 3 }
subject.should_receive(:build_tree).exactly(number_of_categories).times.and_return(group)
group_tree = {}
1.upto(number_of_categories) { |i| group_tree[i] = group }
1.upto(number_of_categories) { |i| group_tree[i][:summary_row] = summary_columns_object }
subject.group_and_sort(@rule1, @remaining_rules, @items).should == group_tree
end
end
context "building the table Array" do
before(:each) do
rule1 = double(:rule1)
rule2 = double(:rule2)
@rules = [rule1, rule2]
@column1 = double(:col1, :call => "Column1")
@column2 = double(:col2, :call => "Column2")
@columns = [@column1, @column2]
sumcol1 = double(:sumcol1, :call => "SumColumn1")
sumcol2 = double(:sumcol2, :call => "SumColumn2")
@sumcols = [sumcol1, sumcol2]
item1 = double(:item1)
item2 = double(:item2)
item3 = double(:item3)
@items1 = [item1, item2]
@items2 = [item2, item3]
@items3 = [item3, item1]
end
it "should return columns when given an Array" do
subject = OrderGrouper.new @rules, @columns
@column1.should_receive(:call)
@column2.should_receive(:call)
subject.build_table(@items1).should == [["Column1", "Column2"]]
end
it "should return a row for each key-value pair when given a Hash" do
groups = { items1: @items1, items2: @items2, items3: @items3 }
subject = OrderGrouper.new @rules, @columns
#subject.should_receive(:build_table).exactly(2).times
expected_return = []
groups.length.times { expected_return << ["Column1", "Column2"] }
subject.build_table(groups).should == expected_return
end
it "should return an extra row when a :summary_row key appears in a given Hash" do
groups = { items1: @items1, items2: @items2, items3: @items3, summary_row: { items: { items2: @items2, items3: @items3 }, columns: @sumcols } }
subject = OrderGrouper.new @rules, @columns
expected_return = []
groups.each do |key, group|
if key == :summary_row
expected_return << ["SumColumn1", "SumColumn2"]
else
expected_return << ["Column1", "Column2"]
end
end
subject.build_table(groups).should == expected_return
end
end
end
end

View File

@@ -49,14 +49,14 @@ feature %q{
fill_in 'product_price', :with => '19.99'
select 'New supplier', :from => 'product_supplier_id'
choose 'product_group_buy_1'
fill_in 'Group buy unit size', :with => '10 kg'
fill_in 'Group buy unit size', :with => '10'
click_button 'Create'
flash_message.should == 'Product "A new product !!!" has been successfully created!'
product = Spree::Product.find_by_name('A new product !!!')
product.group_buy.should be_true
product.group_buy_unit_size.should == '10 kg'
product.group_buy_unit_size.should == 10.0
end
end

View File

@@ -24,4 +24,28 @@ feature %q{
page.should have_content 'Supplier'
end
scenario "bulk co-op report" do
login_to_admin_section
click_link 'Reports'
click_link 'Bulk Co-Op'
page.should have_content 'Supplier'
end
scenario "payments reports" do
login_to_admin_section
click_link 'Reports'
click_link 'Payment Reports'
page.should have_content 'Payment State'
end
scenario "order cycle reports" do
login_to_admin_section
click_link 'Reports'
click_link 'Order Cycle Reports'
page.should have_content 'Supplier'
end
end

View File

@@ -22,4 +22,62 @@ feature %q{
page.should have_selector 'td', :text => d.name
end
describe "viewing distributor details" do
context "without Javascript" do
it "displays a holding message when no distributor is selected" do
p = create(:product)
visit spree.product_path p
page.should have_selector '#product-distributor-details', :text => 'When you select a distributor for your order, their address and pickup times will be displayed here.'
end
it "displays distributor details when one is selected" do
d = create(:distributor_enterprise)
p = create(:product, :distributors => [d])
visit spree.root_path
click_link d.name
visit spree.product_path p
within '#product-distributor-details' do
[d.name,
d.address.address1,
d.address.city,
d.address.zipcode,
d.address.state_text,
d.address.country.name,
d.pickup_times,
d.next_collection_at,
d.contact,
d.phone,
d.email,
d.description,
d.website].each do |value|
page.should have_content value
end
end
end
end
context "with Javascript", js: true do
it "changes distributor details when the distributor is changed" do
d1 = create(:distributor_enterprise)
d2 = create(:distributor_enterprise)
d3 = create(:distributor_enterprise)
p = create(:product, :distributors => [d1, d2, d3])
visit spree.product_path p
[d1, d2, d3].each do |d|
select d.name, :from => 'distributor_id'
within '#product-distributor-details' do
page.should have_selector 'h2', :text => d.name
end
end
end
end
end
end