From 38e1bd413998b7eccf2d31716f9adc9085e4053e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 1 May 2015 15:44:19 +1000 Subject: [PATCH 01/30] Fix indentation --- lib/open_food_network/order_and_distributor_report.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/open_food_network/order_and_distributor_report.rb b/lib/open_food_network/order_and_distributor_report.rb index 2662b176dd..011e8d19fe 100644 --- a/lib/open_food_network/order_and_distributor_report.rb +++ b/lib/open_food_network/order_and_distributor_report.rb @@ -1,4 +1,3 @@ - module OpenFoodNetwork class OrderAndDistributorReport @@ -8,14 +7,15 @@ module OpenFoodNetwork def header ["Order date", "Order Id", - "Customer Name","Customer Email", "Customer Phone", "Customer City", - "SKU", "Item name", "Variant", "Quantity", "Max Quantity", "Cost", "Shipping cost", - "Payment method", - "Distributor", "Distributor address", "Distributor city", "Distributor postcode", "Shipping instructions"] + "Customer Name","Customer Email", "Customer Phone", "Customer City", + "SKU", "Item name", "Variant", "Quantity", "Max Quantity", "Cost", "Shipping cost", + "Payment method", + "Distributor", "Distributor address", "Distributor city", "Distributor postcode", "Shipping instructions"] end def table order_and_distributor_details = [] + @orders.each do |order| order.line_items.each do |line_item| order_and_distributor_details << [order.created_at, order.id, @@ -25,6 +25,7 @@ module OpenFoodNetwork order.distributor.andand.name, order.distributor.address.address1, order.distributor.address.city, order.distributor.address.zipcode, order.special_instructions ] end end + order_and_distributor_details end end From 0a2f2e0fbaa5b393bc8b4ed1d15425784db7a111 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 7 May 2015 15:31:39 +1000 Subject: [PATCH 02/30] Output summary data without customisation Add route for xero invoices report Add require for reports controller decorator --- .../admin/reports_controller_decorator.rb | 11 +++- config/routes.rb | 1 + lib/open_food_network/xero_invoices_report.rb | 60 +++++++++++++++++++ spec/features/admin/reports_spec.rb | 60 +++++++++++++++++++ 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 lib/open_food_network/xero_invoices_report.rb diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 3aa4f560a1..3ef32aa074 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -7,6 +7,7 @@ require 'open_food_network/customers_report' require 'open_food_network/users_and_enterprises_report' require 'open_food_network/order_cycle_management_report' require 'open_food_network/sales_tax_report' +require 'open_food_network/xero_invoices_report' Spree::Admin::ReportsController.class_eval do @@ -679,7 +680,15 @@ Spree::Admin::ReportsController.class_eval do render_report(@report.header, @report.table, params[:csv], "users_and_enterprises_#{timestamp}.csv") end - def render_report (header, table, create_csv, csv_file_name) + def xero_invoices + @search = Spree::Order.complete.managed_by(spree_current_user).search(params[:q]) + orders = @search.result + @report = OpenFoodNetwork::XeroInvoicesReport.new orders + render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv") + end + + + def render_report(header, table, create_csv, csv_file_name) unless create_csv render :html => table else diff --git a/config/routes.rb b/config/routes.rb index 4621ee4a35..66805807ce 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -134,6 +134,7 @@ Spree::Core::Engine.routes.prepend do match '/admin/orders/bulk_management' => 'admin/orders#bulk_management', :as => "admin_bulk_order_management" match '/admin/reports/products_and_inventory' => 'admin/reports#products_and_inventory', :as => "products_and_inventory_admin_reports", :via => [:get, :post] match '/admin/reports/customers' => 'admin/reports#customers', :as => "customers_admin_reports", :via => [:get, :post] + match '/admin/reports/xero_invoices' => 'admin/reports#xero_invoices', :as => "xero_invoices_admin_reports", :via => [:get, :post] match '/admin', :to => 'admin/overview#index', :as => :admin match '/admin/payment_methods/show_provider_preferences' => 'admin/payment_methods#show_provider_preferences', :via => :get diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb new file mode 100644 index 0000000000..18c95073d3 --- /dev/null +++ b/lib/open_food_network/xero_invoices_report.rb @@ -0,0 +1,60 @@ +module OpenFoodNetwork + class XeroInvoicesReport + def initialize(orders) + @orders = orders + end + + def header + %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme) + end + + def table + rows = [] + + @orders.each do |order| + rows << summary_row(order, 'Total untaxable produce (no tax)', 0, 'GST Free Income') + rows << summary_row(order, 'Total taxable produce (tax inclusive)', 0, 'GST on Income') + rows << summary_row(order, 'Total untaxable fees (no tax)', 0, 'GST Free Income') + rows << summary_row(order, 'Total taxable fees (tax inclusive)', 0, 'GST on Income') + rows << summary_row(order, 'Delivery Shipping Cost (tax inclusive)', 0, 'Tax or No Tax - depending on enterprise setting') + end + + rows + end + + + private + + def summary_row(order, description, amount, tax_type) + [order.bill_address.full_name, + order.email, + order.bill_address.address1, + order.bill_address.address2, + '', + '', + order.bill_address.city, + order.bill_address.state, + order.bill_address.zipcode, + order.bill_address.country.andand.name, + order.number, # To customise + order.number, + Date.today, # To customise + 2.weeks.from_now.to_date, # To customise + '', + description, + '1', + amount, + '', + 'food sales', # To customise + tax_type, + '', + '', + '', + '', + Spree::Config.currency, + '' + ] + end + + end +end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 309e0a3e81..70e7711759 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -298,4 +298,64 @@ feature %q{ ].sort end end + + describe "Xero invoices report" do + let(:distributor1) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } + let(:distributor2) { create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true) } + let(:user1) { create_enterprise_user enterprises: [distributor1] } + let(:user2) { create_enterprise_user enterprises: [distributor2] } + let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } + let(:enterprise_fee) { create(:enterprise_fee, enterprise: user1.enterprises.first, tax_category: product2.tax_category, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 120.0)) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: distributor1, coordinator_fees: [enterprise_fee], distributors: [distributor1], variants: [product1.master]) } + + let!(:zone) { create(:zone_with_member) } + let(:country) { Spree::Country.find Spree::Config.default_country_id } + let(:bill_address) { create(:address, firstname: 'Customer', lastname: 'Name', address1: 'customer l1', address2: '', city: 'customer city', zipcode: 1234, country: country) } + let(:order1) { create(:order, order_cycle: order_cycle, distributor: user1.enterprises.first, shipping_method: shipping_method, bill_address: bill_address) } + let(:product1) { create(:taxed_product, zone: zone, price: 12.54, tax_rate_amount: 0) } + let(:product2) { create(:taxed_product, zone: zone, price: 500.15, tax_rate_amount: 0.2) } + + let!(:line_item1) { create(:line_item, variant: product1.master, price: 12.54, quantity: 1, order: order1) } + let!(:line_item2) { create(:line_item, variant: product2.master, price: 500.15, quantity: 3, order: order1) } + + let!(:adj_shipping) { create(:adjustment, adjustable: order1, label: "Shipping", amount: 100.55) } + + before do + order1.update_attribute :email, 'customer@email.com' + Timecop.travel(Time.zone.local(2015, 4, 25, 14, 0, 0)) { order1.finalize! } + + login_to_admin_section + click_link 'Reports' + + click_link 'Xero invoices' + end + + around do |example| + Timecop.travel(Time.zone.local(2015, 4, 26, 14, 0, 0)) do + example.yield + end + end + + it "shows Xero invoices report" do + rows = find("table#listing_invoices").all("tr") + table = rows.map { |r| r.all("th,td").map { |c| c.text.strip } } + + table.should == [ + %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme), + xero_invoice_row('Total untaxable produce (no tax)', 0, 'GST Free Income'), + xero_invoice_row('Total taxable produce (tax inclusive)', 0, 'GST on Income'), + xero_invoice_row('Total untaxable fees (no tax)', 0, 'GST Free Income'), + xero_invoice_row('Total taxable fees (tax inclusive)', 0, 'GST on Income'), + xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 0, 'Tax or No Tax - depending on enterprise setting') + ] + end + + + private + + def xero_invoice_row(description, amount, tax_type) + ['Customer Name', 'customer@email.com', 'customer l1', '', '', '', 'customer city', 'Victoria', '1234', country.name, order1.number, order1.number, '2015-04-26', '2015-05-10', '', description, '1', amount.to_s, '', 'food sales', tax_type, '', '', '', '', Spree::Config.currency, ''] + + end + end end From 0737ac8da02f253a1c7a5e766240d90090c9c87b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 14 May 2015 12:11:38 +1000 Subject: [PATCH 03/30] Write an rspec table matcher that gives informative error messages --- spec/support/matchers/table_matchers.rb | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/spec/support/matchers/table_matchers.rb b/spec/support/matchers/table_matchers.rb index 053562b9e4..411b0b646f 100644 --- a/spec/support/matchers/table_matchers.rb +++ b/spec/support/matchers/table_matchers.rb @@ -26,3 +26,44 @@ RSpec::Matchers.define :have_table_row do |row| node.all('tr').map { |tr| tr.all('th, td').map(&:text) } end end + + + +# find("#my-table").should match_table [[...]] +RSpec::Matchers.define :match_table do |expected_table| + + match_for_should do |node| + rows = node. + all("tr"). + map { |r| r.all("th,td").map { |c| c.text.strip } } + + if rows.count != expected_table.count + @failure_message = "found table with #{rows.count} rows, expected #{expected_table.count}" + + else + rows.each_with_index do |row, i| + expected_row = expected_table[i] + if row.count != expected_row.count + @failure_message = "row #{i} has #{row.count} columns, expected #{expected_row.count}" + break + + elsif row != expected_row + row.each_with_index do |cell, j| + if cell != expected_row[j] + @failure_message = "cell [#{i}, #{j}] has content '#{cell}', expected '#{expected_row[j]}'" + break + end + end + break if @failure_message + end + end + end + + @failure_message.nil? + end + + failure_message_for_should do |text| + @failure_message + end + +end From c5b618b1f4589f8243da2455e509fc012c2ebc09 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 14 May 2015 12:25:48 +1000 Subject: [PATCH 04/30] Admin can customise some fields on Xero invoices report Add require for xero invoices report spec --- .../admin/reports_controller_decorator.rb | 6 ++- .../admin/reports/xero_invoices.html.haml | 33 +++++++++++++++ lib/open_food_network/xero_invoices_report.rb | 32 +++++++++------ spec/features/admin/reports_spec.rb | 40 +++++++++++++++---- .../xero_invoices_report_spec.rb | 25 ++++++++++++ 5 files changed, 115 insertions(+), 21 deletions(-) create mode 100644 app/views/spree/admin/reports/xero_invoices.html.haml create mode 100644 spec/lib/open_food_network/xero_invoices_report_spec.rb diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 3ef32aa074..15fab60d89 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -683,7 +683,7 @@ Spree::Admin::ReportsController.class_eval do def xero_invoices @search = Spree::Order.complete.managed_by(spree_current_user).search(params[:q]) orders = @search.result - @report = OpenFoodNetwork::XeroInvoicesReport.new orders + @report = OpenFoodNetwork::XeroInvoicesReport.new orders, params render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv") end @@ -725,7 +725,9 @@ Spree::Admin::ReportsController.class_eval do :sales_total => { :name => "Sales Total", :description => "Sales Total For All Orders" }, :users_and_enterprises => { :name => "Users & Enterprises", :description => "Enterprise Ownership & Status" }, :order_cycle_management => {:name => "Order Cycle Management", :description => ''}, - :sales_tax => { :name => "Sales Tax", :description => "Sales Tax For Orders" } + :sales_tax => { :name => "Sales Tax", :description => "Sales Tax For Orders" }, + :xero_invoices => { :name => "Xero Invoices", :description => 'Invoices for import into Xero' } + } # Return only reports the user is authorized to view. reports.select { |action| can? action, :report } diff --git a/app/views/spree/admin/reports/xero_invoices.html.haml b/app/views/spree/admin/reports/xero_invoices.html.haml new file mode 100644 index 0000000000..8c11e5f543 --- /dev/null +++ b/app/views/spree/admin/reports/xero_invoices.html.haml @@ -0,0 +1,33 @@ += form_tag spree.xero_invoices_admin_reports_path do + .row + .four.columns.alpha= label_tag :initial_invoice_number, "Initial invoice number:" + .twelve.columns.omega= text_field_tag :initial_invoice_number + .row + .four.columns.alpha= label_tag :invoice_date, "Invoice date:" + .twelve.columns.omega= text_field_tag :invoice_date, '', class: 'datetimepicker' + .row + .four.columns.alpha= label_tag :due_date, "Due date:" + .twelve.columns.omega= text_field_tag :due_date, '', class: 'datetimepicker' + .row + .four.columns.alpha= label_tag :account_code, "Account code:" + .twelve.columns.omega= text_field_tag :account_code + .row + .four.columns.alpha= label_tag :csv, "Download as CSV:" + .twelve.columns.omega= check_box_tag :csv + .row + .four.columns.alpha= button t(:search) + + +%table#listing_invoices.index + %thead + %tr + - @report.header.each do |header| + %th= header + %tbody + - @report.table.each do |row| + %tr + - row.each do |column| + %td= column + - if @report.table.empty? + %tr + %td{:colspan => "2"}= t(:none) diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb index 18c95073d3..784550a4b4 100644 --- a/lib/open_food_network/xero_invoices_report.rb +++ b/lib/open_food_network/xero_invoices_report.rb @@ -1,7 +1,10 @@ module OpenFoodNetwork class XeroInvoicesReport - def initialize(orders) + def initialize(orders, opts={}) @orders = orders + @opts = opts.reverse_merge({invoice_date: Date.today, + due_date: 2.weeks.from_now.to_date, + account_code: 'food sales'}) end def header @@ -11,12 +14,13 @@ module OpenFoodNetwork def table rows = [] - @orders.each do |order| - rows << summary_row(order, 'Total untaxable produce (no tax)', 0, 'GST Free Income') - rows << summary_row(order, 'Total taxable produce (tax inclusive)', 0, 'GST on Income') - rows << summary_row(order, 'Total untaxable fees (no tax)', 0, 'GST Free Income') - rows << summary_row(order, 'Total taxable fees (tax inclusive)', 0, 'GST on Income') - rows << summary_row(order, 'Delivery Shipping Cost (tax inclusive)', 0, 'Tax or No Tax - depending on enterprise setting') + @orders.each_with_index do |order, i| + invoice_number = invoice_number_for(order, i) + rows << summary_row(order, 'Total untaxable produce (no tax)', 0, invoice_number, 'GST Free Income', @opts) + rows << summary_row(order, 'Total taxable produce (tax inclusive)', 0, invoice_number, 'GST on Income', @opts) + rows << summary_row(order, 'Total untaxable fees (no tax)', 0, invoice_number, 'GST Free Income', @opts) + rows << summary_row(order, 'Total taxable fees (tax inclusive)', 0, invoice_number, 'GST on Income', @opts) + rows << summary_row(order, 'Delivery Shipping Cost (tax inclusive)', 0, invoice_number, 'Tax or No Tax - depending on enterprise setting', @opts) end rows @@ -25,7 +29,11 @@ module OpenFoodNetwork private - def summary_row(order, description, amount, tax_type) + def invoice_number_for(order, i) + @opts[:initial_invoice_number] ? @opts[:initial_invoice_number].to_i+i : order.number + end + + def summary_row(order, description, amount, invoice_number, tax_type, opts={}) [order.bill_address.full_name, order.email, order.bill_address.address1, @@ -36,16 +44,16 @@ module OpenFoodNetwork order.bill_address.state, order.bill_address.zipcode, order.bill_address.country.andand.name, - order.number, # To customise + invoice_number, order.number, - Date.today, # To customise - 2.weeks.from_now.to_date, # To customise + opts[:invoice_date], + opts[:due_date], '', description, '1', amount, '', - 'food sales', # To customise + opts[:account_code], tax_type, '', '', diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 70e7711759..49a0979406 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -327,7 +327,7 @@ feature %q{ login_to_admin_section click_link 'Reports' - click_link 'Xero invoices' + click_link 'Xero Invoices' end around do |example| @@ -337,10 +337,7 @@ feature %q{ end it "shows Xero invoices report" do - rows = find("table#listing_invoices").all("tr") - table = rows.map { |r| r.all("th,td").map { |c| c.text.strip } } - - table.should == [ + xero_invoice_table.should match_table [ %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme), xero_invoice_row('Total untaxable produce (no tax)', 0, 'GST Free Income'), xero_invoice_row('Total taxable produce (tax inclusive)', 0, 'GST on Income'), @@ -350,11 +347,40 @@ feature %q{ ] end + it "can customise a number of fields" do + fill_in 'initial_invoice_number', with: '5' + fill_in 'invoice_date', with: '2015-02-12' + fill_in 'due_date', with: '2015-03-12' + fill_in 'account_code', with: 'abc123' + click_button 'Search' + + opts = {invoice_number: '5', invoice_date: '2015-02-12', due_date: '2015-03-12', account_code: 'abc123'} + + xero_invoice_table.should match_table [ + %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme), + xero_invoice_row('Total untaxable produce (no tax)', 0, 'GST Free Income', opts), + xero_invoice_row('Total taxable produce (tax inclusive)', 0, 'GST on Income', opts), + xero_invoice_row('Total untaxable fees (no tax)', 0, 'GST Free Income', opts), + xero_invoice_row('Total taxable fees (tax inclusive)', 0, 'GST on Income', opts), + xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 0, 'Tax or No Tax - depending on enterprise setting', opts) + ] + + # TODO: + # - Amounts + # - Tax specification for shipping + end + private - def xero_invoice_row(description, amount, tax_type) - ['Customer Name', 'customer@email.com', 'customer l1', '', '', '', 'customer city', 'Victoria', '1234', country.name, order1.number, order1.number, '2015-04-26', '2015-05-10', '', description, '1', amount.to_s, '', 'food sales', tax_type, '', '', '', '', Spree::Config.currency, ''] + def xero_invoice_table + find("table#listing_invoices") + end + + def xero_invoice_row(description, amount, tax_type, opts={}) + opts.reverse_merge!({invoice_number: order1.number, invoice_date: '2015-04-26', due_date: '2015-05-10', account_code: 'food sales'}) + + ['Customer Name', 'customer@email.com', 'customer l1', '', '', '', 'customer city', 'Victoria', '1234', country.name, opts[:invoice_number], order1.number, opts[:invoice_date], opts[:due_date], '', description, '1', amount.to_s, '', opts[:account_code], tax_type, '', '', '', '', Spree::Config.currency, ''] end end diff --git a/spec/lib/open_food_network/xero_invoices_report_spec.rb b/spec/lib/open_food_network/xero_invoices_report_spec.rb new file mode 100644 index 0000000000..9756629f0e --- /dev/null +++ b/spec/lib/open_food_network/xero_invoices_report_spec.rb @@ -0,0 +1,25 @@ +require 'open_food_network/xero_invoices_report' + +module OpenFoodNetwork + describe XeroInvoicesReport do + subject { XeroInvoicesReport.new [] } + + describe "generating invoice numbers" do + let(:order) { double(:order, number: 'R731032860') } + + describe "when no initial invoice number is given" do + it "returns the order number" do + subject.send(:invoice_number_for, order, 123).should == 'R731032860' + end + end + + describe "when an initial invoice number is given" do + subject { XeroInvoicesReport.new [], {initial_invoice_number: '123'} } + + it "increments the number by the index" do + subject.send(:invoice_number_for, order, 456).should == 579 + end + end + end + end +end From 5660e3737e2e5fd82b01fb2dcba7daf22e305704 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 14 May 2015 13:01:46 +1000 Subject: [PATCH 05/30] Extract order rows generation to method --- lib/open_food_network/xero_invoices_report.rb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb index 784550a4b4..7308f7740b 100644 --- a/lib/open_food_network/xero_invoices_report.rb +++ b/lib/open_food_network/xero_invoices_report.rb @@ -16,11 +16,7 @@ module OpenFoodNetwork @orders.each_with_index do |order, i| invoice_number = invoice_number_for(order, i) - rows << summary_row(order, 'Total untaxable produce (no tax)', 0, invoice_number, 'GST Free Income', @opts) - rows << summary_row(order, 'Total taxable produce (tax inclusive)', 0, invoice_number, 'GST on Income', @opts) - rows << summary_row(order, 'Total untaxable fees (no tax)', 0, invoice_number, 'GST Free Income', @opts) - rows << summary_row(order, 'Total taxable fees (tax inclusive)', 0, invoice_number, 'GST on Income', @opts) - rows << summary_row(order, 'Delivery Shipping Cost (tax inclusive)', 0, invoice_number, 'Tax or No Tax - depending on enterprise setting', @opts) + rows += rows_for_order(order, invoice_number, @opts) end rows @@ -29,6 +25,17 @@ module OpenFoodNetwork private + def rows_for_order(order, invoice_number, opts) + [ + summary_row(order, 'Total untaxable produce (no tax)', 0, invoice_number, 'GST Free Income', opts), + summary_row(order, 'Total taxable produce (tax inclusive)', 0, invoice_number, 'GST on Income', opts), + summary_row(order, 'Total untaxable fees (no tax)', 0, invoice_number, 'GST Free Income', opts), + summary_row(order, 'Total taxable fees (tax inclusive)', 0, invoice_number, 'GST on Income', opts), + summary_row(order, 'Delivery Shipping Cost (tax inclusive)', 0, invoice_number, 'Tax or No Tax - depending on enterprise setting', opts) + ] + end + + def invoice_number_for(order, i) @opts[:initial_invoice_number] ? @opts[:initial_invoice_number].to_i+i : order.number end From ca1d88d8b142d015edbc71e8a9de961d3db564d8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 11:49:24 +1000 Subject: [PATCH 06/30] Find line items with and without tax --- app/models/spree/line_item_decorator.rb | 9 +++++++++ spec/models/spree/line_item_spec.rb | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index b2c0a583fc..0d6532327c 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -23,6 +23,15 @@ Spree::LineItem.class_eval do where('spree_products.supplier_id IN (?)', enterprises) } + scope :with_tax, joins(:adjustments). + where('spree_adjustments.originator_type = ?', 'Spree::TaxRate'). + select('DISTINCT spree_line_items.*') + + # Line items without a Spree::TaxRate-originated adjustment + scope :without_tax, joins("LEFT OUTER JOIN spree_adjustments ON (spree_adjustments.adjustable_id=spree_line_items.id AND spree_adjustments.adjustable_type = 'Spree::LineItem' AND spree_adjustments.originator_type='Spree::TaxRate')"). + where('spree_adjustments.id IS NULL') + + def price_with_adjustments # EnterpriseFee#create_locked_adjustment applies adjustments on line items to their parent order, # so line_item.adjustments returns an empty array diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index 6166732b76..b39b51e1a5 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -24,6 +24,22 @@ module Spree LineItem.supplied_by_any([s2]).should == [li2] LineItem.supplied_by_any([s1, s2]).sort.should == [li1, li2].sort end + + describe "finding line items with and without tax" do + let(:tax_rate) { create(:tax_rate, calculator: Spree::Calculator::DefaultTax.new) } + let!(:adjustment1) { create(:adjustment, adjustable: li1, originator: tax_rate, label: "TR", amount: 123, included_tax: 10.00) } + let!(:adjustment2) { create(:adjustment, adjustable: li1, originator: tax_rate, label: "TR", amount: 123, included_tax: 10.00) } + + before { li1; li2 } + + it "finds line items with tax" do + LineItem.with_tax.should == [li1] + end + + it "finds line items without tax" do + LineItem.without_tax.should == [li2] + end + end end describe "calculating price with adjustments" do From 0dcd8eb8cc9df25af785b09a26cb21bc5edbd2f8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 11:57:01 +1000 Subject: [PATCH 07/30] Find adjustments with and without tax --- app/models/spree/adjustment_decorator.rb | 2 ++ spec/models/spree/adjustment_spec.rb | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/app/models/spree/adjustment_decorator.rb b/app/models/spree/adjustment_decorator.rb index 836080183c..153cc10a82 100644 --- a/app/models/spree/adjustment_decorator.rb +++ b/app/models/spree/adjustment_decorator.rb @@ -4,6 +4,8 @@ module Spree scope :enterprise_fee, where(originator_type: 'EnterpriseFee') scope :included_tax, where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::LineItem') + scope :with_tax, where('spree_adjustments.included_tax > 0') + scope :without_tax, where('spree_adjustments.included_tax = 0') attr_accessible :included_tax diff --git a/spec/models/spree/adjustment_spec.rb b/spec/models/spree/adjustment_spec.rb index 579965aa7a..bd952f2e9c 100644 --- a/spec/models/spree/adjustment_spec.rb +++ b/spec/models/spree/adjustment_spec.rb @@ -5,6 +5,21 @@ module Spree adjustment.metadata.should be end + describe "finding adjustments with and without tax included" do + let!(:adjustment_with_tax) { create(:adjustment, included_tax: 123) } + let!(:adjustment_without_tax) { create(:adjustment, included_tax: 0) } + + it "finds adjustments with tax" do + Adjustment.with_tax.should include adjustment_with_tax + Adjustment.with_tax.should_not include adjustment_without_tax + end + + it "finds adjustments without tax" do + Adjustment.without_tax.should include adjustment_without_tax + Adjustment.without_tax.should_not include adjustment_with_tax + end + end + describe "recording included tax" do describe "TaxRate adjustments" do let!(:zone) { create(:zone_with_member) } From dc8270ed72971a490f444fdc6d06f491d8022706 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 12:16:35 +1000 Subject: [PATCH 08/30] Display amounts on xero invoice report --- lib/open_food_network/xero_invoices_report.rb | 30 +++++++++++++++--- spec/features/admin/reports_spec.rb | 31 ++++++++++--------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb index 7308f7740b..5b346f9263 100644 --- a/lib/open_food_network/xero_invoices_report.rb +++ b/lib/open_food_network/xero_invoices_report.rb @@ -27,15 +27,35 @@ module OpenFoodNetwork def rows_for_order(order, invoice_number, opts) [ - summary_row(order, 'Total untaxable produce (no tax)', 0, invoice_number, 'GST Free Income', opts), - summary_row(order, 'Total taxable produce (tax inclusive)', 0, invoice_number, 'GST on Income', opts), - summary_row(order, 'Total untaxable fees (no tax)', 0, invoice_number, 'GST Free Income', opts), - summary_row(order, 'Total taxable fees (tax inclusive)', 0, invoice_number, 'GST on Income', opts), - summary_row(order, 'Delivery Shipping Cost (tax inclusive)', 0, invoice_number, 'Tax or No Tax - depending on enterprise setting', opts) + summary_row(order, 'Total untaxable produce (no tax)', total_untaxable_products(order), invoice_number, 'GST Free Income', opts), + summary_row(order, 'Total taxable produce (tax inclusive)', total_taxable_products(order), invoice_number, 'GST on Income', opts), + summary_row(order, 'Total untaxable fees (no tax)', total_untaxable_fees(order), invoice_number, 'GST Free Income', opts), + summary_row(order, 'Total taxable fees (tax inclusive)', total_taxable_fees(order), invoice_number, 'GST on Income', opts), + summary_row(order, 'Delivery Shipping Cost (tax inclusive)', total_shipping(order), invoice_number, 'Tax or No Tax - depending on enterprise setting', opts) ] end + def total_untaxable_products(order) + order.line_items.without_tax.sum &:amount + end + + def total_taxable_products(order) + order.line_items.with_tax.sum &:amount + end + + def total_untaxable_fees(order) + order.adjustments.enterprise_fee.without_tax.sum &:amount + end + + def total_taxable_fees(order) + order.adjustments.enterprise_fee.with_tax.sum &:amount + end + + def total_shipping(order) + order.adjustments.shipping.sum &:amount + end + def invoice_number_for(order, i) @opts[:initial_invoice_number] ? @opts[:initial_invoice_number].to_i+i : order.number end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 49a0979406..2de44d175d 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -305,8 +305,9 @@ feature %q{ let(:user1) { create_enterprise_user enterprises: [distributor1] } let(:user2) { create_enterprise_user enterprises: [distributor2] } let(:shipping_method) { create(:shipping_method, name: "Shipping", description: "Expensive", calculator: Spree::Calculator::FlatRate.new(preferred_amount: 100.55)) } - let(:enterprise_fee) { create(:enterprise_fee, enterprise: user1.enterprises.first, tax_category: product2.tax_category, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 120.0)) } - let(:order_cycle) { create(:simple_order_cycle, coordinator: distributor1, coordinator_fees: [enterprise_fee], distributors: [distributor1], variants: [product1.master]) } + let(:enterprise_fee1) { create(:enterprise_fee, enterprise: user1.enterprises.first, tax_category: product2.tax_category, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 10)) } + let(:enterprise_fee2) { create(:enterprise_fee, enterprise: user1.enterprises.first, tax_category: product2.tax_category, calculator: Spree::Calculator::FlatRate.new(preferred_amount: 20)) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: distributor1, coordinator_fees: [enterprise_fee1, enterprise_fee2], distributors: [distributor1], variants: [product1.master]) } let!(:zone) { create(:zone_with_member) } let(:country) { Spree::Country.find Spree::Config.default_country_id } @@ -318,7 +319,10 @@ feature %q{ let!(:line_item1) { create(:line_item, variant: product1.master, price: 12.54, quantity: 1, order: order1) } let!(:line_item2) { create(:line_item, variant: product2.master, price: 500.15, quantity: 3, order: order1) } - let!(:adj_shipping) { create(:adjustment, adjustable: order1, label: "Shipping", amount: 100.55) } + let!(:adj_shipping) { create(:adjustment, adjustable: order1, label: "Shipping", originator: shipping_method, amount: 100.55) } + let!(:adj_fee1) { create(:adjustment, adjustable: order1, originator: enterprise_fee1, label: "Enterprise fee untaxed", amount: 10, included_tax: 0) } + let!(:adj_fee2) { create(:adjustment, adjustable: order1, originator: enterprise_fee2, label: "Enterprise fee taxed", amount: 20, included_tax: 2) } + before do order1.update_attribute :email, 'customer@email.com' @@ -339,11 +343,11 @@ feature %q{ it "shows Xero invoices report" do xero_invoice_table.should match_table [ %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme), - xero_invoice_row('Total untaxable produce (no tax)', 0, 'GST Free Income'), - xero_invoice_row('Total taxable produce (tax inclusive)', 0, 'GST on Income'), - xero_invoice_row('Total untaxable fees (no tax)', 0, 'GST Free Income'), - xero_invoice_row('Total taxable fees (tax inclusive)', 0, 'GST on Income'), - xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 0, 'Tax or No Tax - depending on enterprise setting') + xero_invoice_row('Total untaxable produce (no tax)', 12.54, 'GST Free Income'), + xero_invoice_row('Total taxable produce (tax inclusive)', 1500.45, 'GST on Income'), + xero_invoice_row('Total untaxable fees (no tax)', 10.0, 'GST Free Income'), + xero_invoice_row('Total taxable fees (tax inclusive)', 20.0, 'GST on Income'), + xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'Tax or No Tax - depending on enterprise setting') ] end @@ -358,15 +362,14 @@ feature %q{ xero_invoice_table.should match_table [ %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme), - xero_invoice_row('Total untaxable produce (no tax)', 0, 'GST Free Income', opts), - xero_invoice_row('Total taxable produce (tax inclusive)', 0, 'GST on Income', opts), - xero_invoice_row('Total untaxable fees (no tax)', 0, 'GST Free Income', opts), - xero_invoice_row('Total taxable fees (tax inclusive)', 0, 'GST on Income', opts), - xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 0, 'Tax or No Tax - depending on enterprise setting', opts) + xero_invoice_row('Total untaxable produce (no tax)', 12.54, 'GST Free Income', opts), + xero_invoice_row('Total taxable produce (tax inclusive)', 1500.45, 'GST on Income', opts), + xero_invoice_row('Total untaxable fees (no tax)', 10.0, 'GST Free Income', opts), + xero_invoice_row('Total taxable fees (tax inclusive)', 20.0, 'GST on Income', opts), + xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'Tax or No Tax - depending on enterprise setting', opts) ] # TODO: - # - Amounts # - Tax specification for shipping end From ca37efdd262277c3b7fbabe9f3f07847c06395fb Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 12:44:48 +1000 Subject: [PATCH 09/30] Display whether there is tax included in shipping --- lib/open_food_network/xero_invoices_report.rb | 15 ++++++++++----- spec/features/admin/reports_spec.rb | 9 +++------ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb index 5b346f9263..504bf03349 100644 --- a/lib/open_food_network/xero_invoices_report.rb +++ b/lib/open_food_network/xero_invoices_report.rb @@ -27,11 +27,11 @@ module OpenFoodNetwork def rows_for_order(order, invoice_number, opts) [ - summary_row(order, 'Total untaxable produce (no tax)', total_untaxable_products(order), invoice_number, 'GST Free Income', opts), - summary_row(order, 'Total taxable produce (tax inclusive)', total_taxable_products(order), invoice_number, 'GST on Income', opts), - summary_row(order, 'Total untaxable fees (no tax)', total_untaxable_fees(order), invoice_number, 'GST Free Income', opts), - summary_row(order, 'Total taxable fees (tax inclusive)', total_taxable_fees(order), invoice_number, 'GST on Income', opts), - summary_row(order, 'Delivery Shipping Cost (tax inclusive)', total_shipping(order), invoice_number, 'Tax or No Tax - depending on enterprise setting', opts) + summary_row(order, 'Total untaxable produce (no tax)', total_untaxable_products(order), invoice_number, 'GST Free Income', opts), + summary_row(order, 'Total taxable produce (tax inclusive)', total_taxable_products(order), invoice_number, 'GST on Income', opts), + summary_row(order, 'Total untaxable fees (no tax)', total_untaxable_fees(order), invoice_number, 'GST Free Income', opts), + summary_row(order, 'Total taxable fees (tax inclusive)', total_taxable_fees(order), invoice_number, 'GST on Income', opts), + summary_row(order, 'Delivery Shipping Cost (tax inclusive)', total_shipping(order), invoice_number, tax_on_shipping_s(order), opts) ] end @@ -56,6 +56,11 @@ module OpenFoodNetwork order.adjustments.shipping.sum &:amount end + def tax_on_shipping_s(order) + tax_on_shipping = order.adjustments.shipping.sum(&:included_tax) > 0 + tax_on_shipping ? 'GST on Income' : 'GST Free Income' + end + def invoice_number_for(order, i) @opts[:initial_invoice_number] ? @opts[:initial_invoice_number].to_i+i : order.number end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 2de44d175d..86cf803fe5 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -319,7 +319,7 @@ feature %q{ let!(:line_item1) { create(:line_item, variant: product1.master, price: 12.54, quantity: 1, order: order1) } let!(:line_item2) { create(:line_item, variant: product2.master, price: 500.15, quantity: 3, order: order1) } - let!(:adj_shipping) { create(:adjustment, adjustable: order1, label: "Shipping", originator: shipping_method, amount: 100.55) } + let!(:adj_shipping) { create(:adjustment, adjustable: order1, label: "Shipping", originator: shipping_method, amount: 100.55, included_tax: 10.06) } let!(:adj_fee1) { create(:adjustment, adjustable: order1, originator: enterprise_fee1, label: "Enterprise fee untaxed", amount: 10, included_tax: 0) } let!(:adj_fee2) { create(:adjustment, adjustable: order1, originator: enterprise_fee2, label: "Enterprise fee taxed", amount: 20, included_tax: 2) } @@ -347,7 +347,7 @@ feature %q{ xero_invoice_row('Total taxable produce (tax inclusive)', 1500.45, 'GST on Income'), xero_invoice_row('Total untaxable fees (no tax)', 10.0, 'GST Free Income'), xero_invoice_row('Total taxable fees (tax inclusive)', 20.0, 'GST on Income'), - xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'Tax or No Tax - depending on enterprise setting') + xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'GST on Income') ] end @@ -366,11 +366,8 @@ feature %q{ xero_invoice_row('Total taxable produce (tax inclusive)', 1500.45, 'GST on Income', opts), xero_invoice_row('Total untaxable fees (no tax)', 10.0, 'GST Free Income', opts), xero_invoice_row('Total taxable fees (tax inclusive)', 20.0, 'GST on Income', opts), - xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'Tax or No Tax - depending on enterprise setting', opts) + xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'GST on Income', opts) ] - - # TODO: - # - Tax specification for shipping end From 3640a71ab8e7b5a7c7dab1bfa8016f5faa981e7e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 15:05:18 +1000 Subject: [PATCH 10/30] Reorder methods --- lib/open_food_network/xero_invoices_report.rb | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb index 504bf03349..9a316e08ed 100644 --- a/lib/open_food_network/xero_invoices_report.rb +++ b/lib/open_food_network/xero_invoices_report.rb @@ -25,6 +25,10 @@ module OpenFoodNetwork private + def invoice_number_for(order, i) + @opts[:initial_invoice_number] ? @opts[:initial_invoice_number].to_i+i : order.number + end + def rows_for_order(order, invoice_number, opts) [ summary_row(order, 'Total untaxable produce (no tax)', total_untaxable_products(order), invoice_number, 'GST Free Income', opts), @@ -35,36 +39,6 @@ module OpenFoodNetwork ] end - - def total_untaxable_products(order) - order.line_items.without_tax.sum &:amount - end - - def total_taxable_products(order) - order.line_items.with_tax.sum &:amount - end - - def total_untaxable_fees(order) - order.adjustments.enterprise_fee.without_tax.sum &:amount - end - - def total_taxable_fees(order) - order.adjustments.enterprise_fee.with_tax.sum &:amount - end - - def total_shipping(order) - order.adjustments.shipping.sum &:amount - end - - def tax_on_shipping_s(order) - tax_on_shipping = order.adjustments.shipping.sum(&:included_tax) > 0 - tax_on_shipping ? 'GST on Income' : 'GST Free Income' - end - - def invoice_number_for(order, i) - @opts[:initial_invoice_number] ? @opts[:initial_invoice_number].to_i+i : order.number - end - def summary_row(order, description, amount, invoice_number, tax_type, opts={}) [order.bill_address.full_name, order.email, @@ -96,5 +70,29 @@ module OpenFoodNetwork ] end + def total_untaxable_products(order) + order.line_items.without_tax.sum &:amount + end + + def total_taxable_products(order) + order.line_items.with_tax.sum &:amount + end + + def total_untaxable_fees(order) + order.adjustments.enterprise_fee.without_tax.sum &:amount + end + + def total_taxable_fees(order) + order.adjustments.enterprise_fee.with_tax.sum &:amount + end + + def total_shipping(order) + order.adjustments.shipping.sum &:amount + end + + def tax_on_shipping_s(order) + tax_on_shipping = order.adjustments.shipping.sum(&:included_tax) > 0 + tax_on_shipping ? 'GST on Income' : 'GST Free Income' + end end end From 3d4a0f84077fd58afdbddfcbdea85efef267882d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 15:27:46 +1000 Subject: [PATCH 11/30] Xero invoices report: Preserve form fields on submit, do not show rows with no cost --- .../spree/admin/reports_controller_decorator.rb | 2 +- app/views/spree/admin/reports/xero_invoices.html.haml | 8 ++++---- lib/open_food_network/xero_invoices_report.rb | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 15fab60d89..df86d224fd 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -681,7 +681,7 @@ Spree::Admin::ReportsController.class_eval do end def xero_invoices - @search = Spree::Order.complete.managed_by(spree_current_user).search(params[:q]) + @search = Spree::Order.complete.managed_by(spree_current_user).order('id DESC').search(params[:q]) orders = @search.result @report = OpenFoodNetwork::XeroInvoicesReport.new orders, params render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv") diff --git a/app/views/spree/admin/reports/xero_invoices.html.haml b/app/views/spree/admin/reports/xero_invoices.html.haml index 8c11e5f543..9433ee91bb 100644 --- a/app/views/spree/admin/reports/xero_invoices.html.haml +++ b/app/views/spree/admin/reports/xero_invoices.html.haml @@ -1,16 +1,16 @@ = form_tag spree.xero_invoices_admin_reports_path do .row .four.columns.alpha= label_tag :initial_invoice_number, "Initial invoice number:" - .twelve.columns.omega= text_field_tag :initial_invoice_number + .twelve.columns.omega= text_field_tag :initial_invoice_number, params[:initial_invoice_number] .row .four.columns.alpha= label_tag :invoice_date, "Invoice date:" - .twelve.columns.omega= text_field_tag :invoice_date, '', class: 'datetimepicker' + .twelve.columns.omega= text_field_tag :invoice_date, params[:invoice_date], class: 'datetimepicker' .row .four.columns.alpha= label_tag :due_date, "Due date:" - .twelve.columns.omega= text_field_tag :due_date, '', class: 'datetimepicker' + .twelve.columns.omega= text_field_tag :due_date, params[:due_date], class: 'datetimepicker' .row .four.columns.alpha= label_tag :account_code, "Account code:" - .twelve.columns.omega= text_field_tag :account_code + .twelve.columns.omega= text_field_tag :account_code, params[:account_code] .row .four.columns.alpha= label_tag :csv, "Download as CSV:" .twelve.columns.omega= check_box_tag :csv diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb index 9a316e08ed..b3d2bc77f3 100644 --- a/lib/open_food_network/xero_invoices_report.rb +++ b/lib/open_food_network/xero_invoices_report.rb @@ -36,10 +36,12 @@ module OpenFoodNetwork summary_row(order, 'Total untaxable fees (no tax)', total_untaxable_fees(order), invoice_number, 'GST Free Income', opts), summary_row(order, 'Total taxable fees (tax inclusive)', total_taxable_fees(order), invoice_number, 'GST on Income', opts), summary_row(order, 'Delivery Shipping Cost (tax inclusive)', total_shipping(order), invoice_number, tax_on_shipping_s(order), opts) - ] + ].compact end def summary_row(order, description, amount, invoice_number, tax_type, opts={}) + return nil if amount == 0 + [order.bill_address.full_name, order.email, order.bill_address.address1, From a467d3c379f3b2f60b982fae662f05ee1df0cfcb Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 16:20:56 +1000 Subject: [PATCH 12/30] Add filtering to xero invoices report - order date range, hub and order cycle --- .../spree/admin/reports_controller_decorator.rb | 7 +++++++ .../spree/admin/reports/xero_invoices.html.haml | 13 ++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index df86d224fd..9633b6cd68 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -681,6 +681,13 @@ Spree::Admin::ReportsController.class_eval do end def xero_invoices + if request.get? + params[:q] ||= {} + params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month + end + @distributors = Enterprise.is_distributor.managed_by(spree_current_user) + @order_cycles = OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC') + @search = Spree::Order.complete.managed_by(spree_current_user).order('id DESC').search(params[:q]) orders = @search.result @report = OpenFoodNetwork::XeroInvoicesReport.new orders, params diff --git a/app/views/spree/admin/reports/xero_invoices.html.haml b/app/views/spree/admin/reports/xero_invoices.html.haml index 9433ee91bb..1ae4e3b279 100644 --- a/app/views/spree/admin/reports/xero_invoices.html.haml +++ b/app/views/spree/admin/reports/xero_invoices.html.haml @@ -1,4 +1,15 @@ -= form_tag spree.xero_invoices_admin_reports_path do += form_for @search, url: spree.xero_invoices_admin_reports_path do |f| + = render 'date_range_form', f: f + + .row + .four.columns.alpha= label_tag nil, "Hub: " + .four.columns.omega= f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth"}) + .row + .four.columns.alpha= label_tag nil, "Order Cycle: " + .four.columns.omega= f.select(:order_cycle_id_eq, + options_for_select(report_order_cycle_options(@order_cycles), params[:q][:order_cycle_id_eq]), + {:include_blank => true}, {:class => "select2 fullwidth"}) + .row .four.columns.alpha= label_tag :initial_invoice_number, "Initial invoice number:" .twelve.columns.omega= text_field_tag :initial_invoice_number, params[:initial_invoice_number] From f7642b2a1b8e6fb19cff2a639f1a44c6f360cc09 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 16:51:04 +1000 Subject: [PATCH 13/30] When blank values are submitted, do not override defaults --- lib/open_food_network/xero_invoices_report.rb | 9 ++++++--- .../open_food_network/xero_invoices_report_spec.rb | 12 ++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb index b3d2bc77f3..79305d655a 100644 --- a/lib/open_food_network/xero_invoices_report.rb +++ b/lib/open_food_network/xero_invoices_report.rb @@ -2,9 +2,12 @@ module OpenFoodNetwork class XeroInvoicesReport def initialize(orders, opts={}) @orders = orders - @opts = opts.reverse_merge({invoice_date: Date.today, - due_date: 2.weeks.from_now.to_date, - account_code: 'food sales'}) + + @opts = opts. + reject { |k, v| v.blank? }. + reverse_merge({invoice_date: Date.today, + due_date: 2.weeks.from_now.to_date, + account_code: 'food sales'}) end def header diff --git a/spec/lib/open_food_network/xero_invoices_report_spec.rb b/spec/lib/open_food_network/xero_invoices_report_spec.rb index 9756629f0e..8551d663a8 100644 --- a/spec/lib/open_food_network/xero_invoices_report_spec.rb +++ b/spec/lib/open_food_network/xero_invoices_report_spec.rb @@ -4,6 +4,18 @@ module OpenFoodNetwork describe XeroInvoicesReport do subject { XeroInvoicesReport.new [] } + describe "option defaults" do + let(:report) { XeroInvoicesReport.new [], {initial_invoice_number: '', invoice_date: '', due_date: '', account_code: ''} } + + around { |example| Timecop.travel(Time.zone.local(2015, 5, 5, 14, 0, 0)) { example.run } } + + it "uses defaults when blank params are passed" do + report.instance_variable_get(:@opts).should == {invoice_date: Date.civil(2015, 5, 5), + due_date: Date.civil(2015, 5, 19), + account_code: 'food sales'} + end + end + describe "generating invoice numbers" do let(:order) { double(:order, number: 'R731032860') } From 70b5ac4785dab82d79853ad43fc48c61dab11c40 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 15 May 2015 16:55:52 +1000 Subject: [PATCH 14/30] Add column for whether the order has been paid for --- lib/open_food_network/xero_invoices_report.rb | 5 +++-- spec/features/admin/reports_spec.rb | 10 +++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb index 79305d655a..1a7f7bd636 100644 --- a/lib/open_food_network/xero_invoices_report.rb +++ b/lib/open_food_network/xero_invoices_report.rb @@ -11,7 +11,7 @@ module OpenFoodNetwork end def header - %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme) + %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme Paid?) end def table @@ -71,7 +71,8 @@ module OpenFoodNetwork '', '', Spree::Config.currency, - '' + '', + order.paid? ? 'Y' : 'N' ] end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index 86cf803fe5..d8f7e1e14d 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -342,7 +342,7 @@ feature %q{ it "shows Xero invoices report" do xero_invoice_table.should match_table [ - %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme), + xero_invoice_header, xero_invoice_row('Total untaxable produce (no tax)', 12.54, 'GST Free Income'), xero_invoice_row('Total taxable produce (tax inclusive)', 1500.45, 'GST on Income'), xero_invoice_row('Total untaxable fees (no tax)', 10.0, 'GST Free Income'), @@ -361,7 +361,7 @@ feature %q{ opts = {invoice_number: '5', invoice_date: '2015-02-12', due_date: '2015-03-12', account_code: 'abc123'} xero_invoice_table.should match_table [ - %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme), + xero_invoice_header, xero_invoice_row('Total untaxable produce (no tax)', 12.54, 'GST Free Income', opts), xero_invoice_row('Total taxable produce (tax inclusive)', 1500.45, 'GST on Income', opts), xero_invoice_row('Total untaxable fees (no tax)', 10.0, 'GST Free Income', opts), @@ -377,10 +377,14 @@ feature %q{ find("table#listing_invoices") end + def xero_invoice_header + %w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4 POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme Paid?) + end + def xero_invoice_row(description, amount, tax_type, opts={}) opts.reverse_merge!({invoice_number: order1.number, invoice_date: '2015-04-26', due_date: '2015-05-10', account_code: 'food sales'}) - ['Customer Name', 'customer@email.com', 'customer l1', '', '', '', 'customer city', 'Victoria', '1234', country.name, opts[:invoice_number], order1.number, opts[:invoice_date], opts[:due_date], '', description, '1', amount.to_s, '', opts[:account_code], tax_type, '', '', '', '', Spree::Config.currency, ''] + ['Customer Name', 'customer@email.com', 'customer l1', '', '', '', 'customer city', 'Victoria', '1234', country.name, opts[:invoice_number], order1.number, opts[:invoice_date], opts[:due_date], '', description, '1', amount.to_s, '', opts[:account_code], tax_type, '', '', '', '', Spree::Config.currency, '', 'N'] end end From 503b687ed1f7ea10a546aac92682671989127a54 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 29 May 2015 15:19:05 +1000 Subject: [PATCH 15/30] Display distributor banner only if current_distributor is present --- app/views/spree/orders/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/spree/orders/show.html.haml b/app/views/spree/orders/show.html.haml index 82120add82..de142e3cac 100644 --- a/app/views/spree/orders/show.html.haml +++ b/app/views/spree/orders/show.html.haml @@ -9,7 +9,7 @@ - else = @order.distributor.next_collection_at - = render "shopping_shared/details" + = render "shopping_shared/details" if current_distributor.present? %fieldset#order_summary{"data-hook" => ""} .row From 678b591c1845f18673476f3d605b6ffda5b84c4a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 29 May 2015 12:43:11 +1000 Subject: [PATCH 16/30] Explain how to disable delayed jobs to send emails again --- config/initializers/delayed_job.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/initializers/delayed_job.rb b/config/initializers/delayed_job.rb index 8fc9aa8ec7..80dc11d3aa 100644 --- a/config/initializers/delayed_job.rb +++ b/config/initializers/delayed_job.rb @@ -2,6 +2,10 @@ Delayed::Worker.logger = Logger.new(Rails.root.join('log', 'delayed_job.log')) Delayed::Worker.destroy_failed_jobs = false Delayed::Worker.max_run_time = 15.minutes +# Uncomment the next line if you want jobs to be executed straight away. +# For example you want emails to be opened in your browser while testing. +#Delayed::Worker.delay_jobs = false + # Notify bugsnag when a job fails # Code adapted from http://trevorturk.com/2011/01/25/notify-hoptoad-if-theres-an-exception-in-delayedjob/ class Delayed::Worker From bf935623dcfbd06c462b9aad5b5093b7d428106f Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 29 May 2015 13:44:53 +1000 Subject: [PATCH 17/30] changing default mailer url in development from test.com to 0.0.0.0:3000 --- config/environments/development.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index efa229e33c..200484122a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -31,7 +31,7 @@ Openfoodnetwork::Application.configure do # Show emails using Letter Opener config.action_mailer.delivery_method = :letter_opener - config.action_mailer.default_url_options = { host: "test.com" } + config.action_mailer.default_url_options = { host: "0.0.0.0:3000" } end From fe27b1d446358231af4c85e5a1ecb9212878a9ea Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 29 May 2015 13:46:16 +1000 Subject: [PATCH 18/30] text changes to the registration wizard --- .../templates/registration/about.html.haml | 2 +- .../templates/registration/finished.html.haml | 2 +- .../registration/introduction.html.haml | 3 +- .../templates/registration/type.html.haml | 8 ++- .../confirmation_instructions.html.haml | 12 ++-- app/views/enterprise_mailer/welcome.html.haml | 71 +++++-------------- .../user_mailer/signup_confirmation.html.haml | 4 +- 7 files changed, 38 insertions(+), 64 deletions(-) diff --git a/app/assets/javascripts/templates/registration/about.html.haml b/app/assets/javascripts/templates/registration/about.html.haml index be9948b95d..9c57240dfe 100644 --- a/app/assets/javascripts/templates/registration/about.html.haml +++ b/app/assets/javascripts/templates/registration/about.html.haml @@ -14,7 +14,7 @@ .small-12.columns .alert-box.info{ "ofn-inline-alert" => true, ng: { show: "visible" } } %h6 Success! {{ enterprise.name }} added to the Open Food Network - %span If you exit the wizard at any stage, login and go to admin to edit or update your enterprise details. + %span If you exit this wizard at any stage, you need to click the confirmation link in the email you have received. This will take you to your admin interface where you can continue setting up your profile. %a.close{ ng: { click: "close()" } } × .small-12.large-8.columns diff --git a/app/assets/javascripts/templates/registration/finished.html.haml b/app/assets/javascripts/templates/registration/finished.html.haml index f647a2d8bb..46cca9daf5 100644 --- a/app/assets/javascripts/templates/registration/finished.html.haml +++ b/app/assets/javascripts/templates/registration/finished.html.haml @@ -18,7 +18,7 @@ %p We've sent a confirmation email to - %strong {{ enterprise.email }}. + %strong {{ enterprise.email }} if it hasn't been activated before. %br Please follow the instructions there to make your enterprise visible on the Open Food Network. %a.button.primary{ type: "button", href: "/" } Open Food Network home > diff --git a/app/assets/javascripts/templates/registration/introduction.html.haml b/app/assets/javascripts/templates/registration/introduction.html.haml index 60a8547b4a..48553de09a 100644 --- a/app/assets/javascripts/templates/registration/introduction.html.haml +++ b/app/assets/javascripts/templates/registration/introduction.html.haml @@ -5,7 +5,7 @@ %h4 %small %i.ofn-i_040-hub - Create your enterprise profile + You can now create a profile for your Producer or Hub .hide-for-large-up %hr %input.button.small.primary{ type: "button", value: "Let's get started!", ng: { click: "select('details')" } } @@ -38,6 +38,7 @@ %strong contact you on the Open Food Network. %p Use this space to tell the story of your enterprise, to help drive connections to your social and online presence. + %p It's also the first step towards trading on the Open Food Network, or opening an online store. .row.show-for-large-up .small-12.columns diff --git a/app/assets/javascripts/templates/registration/type.html.haml b/app/assets/javascripts/templates/registration/type.html.haml index 48d45cb66a..9593bfa89c 100644 --- a/app/assets/javascripts/templates/registration/type.html.haml +++ b/app/assets/javascripts/templates/registration/type.html.haml @@ -38,9 +38,13 @@ %i.ofn-i_013-help   %p Producers make yummy things to eat &/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it. - / %p Hubs connect the producer to the eater. Hubs can be co-ops, independent retailers, buying groups, wholesalers, CSA box schemes, farm-gate stalls, etc. + .panel.callout + .left + %i.ofn-i_013-help +   + %p If you’re not a producer, you’re probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other. .row.buttons .small-12.columns %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('contact')" } } - %input.button.primary.right{ type: "submit", value: "Continue" } + %input.button.primary.right{ type: "submit", value: "Create Profile" } diff --git a/app/views/enterprise_mailer/confirmation_instructions.html.haml b/app/views/enterprise_mailer/confirmation_instructions.html.haml index e957b70b1c..3fe7ba09e9 100644 --- a/app/views/enterprise_mailer/confirmation_instructions.html.haml +++ b/app/views/enterprise_mailer/confirmation_instructions.html.haml @@ -1,20 +1,22 @@ %h3 = "Hi, #{@resource.contact}!" %p.lead - = "Please confirm your email address for " - %strong - = "#{@resource.name}." + = "A profile for #{@resource.name} has been successfully created!" + To activate your Profile we need to confirm this email address. %p   %p.callout - Click the link below to confirm your email and to activate your enterprise. This link can be used only once: + Please click the link below to confirm your email and to continue setting up your profile. %br %strong = link_to 'Confirm this email address »', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %p   %p - = "We're so excited that you're joining the #{ Spree::Config[:site_name] }! Don't hestitate to get in touch if you have any questions." + After confirming your email you can access your administration account for this enterprise. + See the + = link_to 'User Guide', 'http://global.openfoodnetwork.org/platform/user-guide/' + = "to find out more about #{ Spree::Config[:site_name] }'s features and to start using your profile or online store." = render 'shared/mailers/signoff' diff --git a/app/views/enterprise_mailer/welcome.html.haml b/app/views/enterprise_mailer/welcome.html.haml index 5c69caae77..d24cc25904 100644 --- a/app/views/enterprise_mailer/welcome.html.haml +++ b/app/views/enterprise_mailer/welcome.html.haml @@ -1,67 +1,32 @@ %h3 = "Welcome, #{@enterprise.contact}!" %p.lead - Congratulations, + Thank you for confirming your email address. %strong - %strong= @enterprise.name + = @enterprise.name = "is now part of #{ Spree::Config.site_name }!" -/ Heading Panel + +%p + The User Guide with detailed support for setting up your Producer or Hub is here: + = link_to 'Open Food Network User Guide', 'http://global.openfoodnetwork.org/platform/user-guide/' + +%p + You can manage your account by logging into the + = link_to 'Admin Panel', spree.admin_url + or by clicking on the cog in the top right hand side of the homepage, and selecting Administration. + +%p + We also have an online forum for community discussion related to OFN software and the unique challenges of running a food enterprise. You are encouraged to join in. We are constantly evolving and your input into this forum will shape what happens next. + = link_to 'Join the community.', 'http://community.openfoodnetwork.org/' + %p Please find below all the details for viewing and editing your enterprise on %strong= "#{ Spree::Config.site_name }." We suggest keeping this email and information somewhere safe. Logging in with the account details below will allow complete access to your products and services. --#%p   - --# %p.callout --# %strong --# Your enterprise details --# %table{:width => "100%"} --# %tr --# %td{:align => "right"} --# %strong --# Shop URL --# %td   --# %td --# %a{:href => "#{ main_app.enterprise_shop_url(@enterprise) }", :target => "_blank"} --# = main_app.enterprise_shop_url(@enterprise) --# %tr --# %td   --# %tr --# %td{:align => "right"} --# %strong --# Email --# %td   --# %td --# %a{:href => "mailto:#{ @enterprise.email }", :target => "_blank"} --# = @enterprise.email - -%p   %p - Log into - %strong= "#{ Spree::Config.site_name } Admin" - in order to edit your enterprise details such as website and social media links, or to start adding products to your enterprise! - -%p.callout - %strong - OFN Admin -%table{ :width => "100%"} - %tr - %td{:align => "right"} - %strong - Admin - %td   - %td - %a{:href => "#{ spree.admin_url }", :target => "_blank"} - = spree.admin_url - -%p   -/ /Heading Panel -%p - We're so pleased to have you as a valued member of - %strong= "#{Spree::Config.site_name}!" - Don't hestitate to get in touch if you have any questions. + If you have any difficulties, check out our FAQs, browse the forum or post a 'Support' topic and someone will help you out! = render 'shared/mailers/signoff' -= render 'shared/mailers/social_and_contact' \ No newline at end of file += render 'shared/mailers/social_and_contact' diff --git a/app/views/spree/user_mailer/signup_confirmation.html.haml b/app/views/spree/user_mailer/signup_confirmation.html.haml index fb52f57119..c85b377fa4 100644 --- a/app/views/spree/user_mailer/signup_confirmation.html.haml +++ b/app/views/spree/user_mailer/signup_confirmation.html.haml @@ -21,7 +21,9 @@ %hr/ %p   %p.lead - Thanks for joining the network. We look forward to introducing you to many fantastic farmers, wonderful food hubs and delicious food! + Thanks for joining the network. + If you are a customer, we look forward to introducing you to many fantastic farmers, wonderful food hubs and delicious food! + If you are a producer or food enterprise, we are excited to have you as a part of the network. %p We welcome all your questions and feedback; you can use the %em From cb2adea59f4db437edbe40cc5167682f45cbd405 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 3 Jun 2015 10:16:29 +1000 Subject: [PATCH 19/30] Remove executable bit from a migration file --- db/migrate/20120327000593_add_addresses_checkouts_indexes.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 db/migrate/20120327000593_add_addresses_checkouts_indexes.rb diff --git a/db/migrate/20120327000593_add_addresses_checkouts_indexes.rb b/db/migrate/20120327000593_add_addresses_checkouts_indexes.rb old mode 100755 new mode 100644 From 96177b7cd7092ed2c79abc23eacf953e2836c12e Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 3 Jun 2015 11:57:22 +1000 Subject: [PATCH 20/30] Add unique index to enterprise permalink --- ...3_add_unique_index_to_enterprise_permalink.rb | 16 ++++++++++++++++ db/schema.rb | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20150603001843_add_unique_index_to_enterprise_permalink.rb diff --git a/db/migrate/20150603001843_add_unique_index_to_enterprise_permalink.rb b/db/migrate/20150603001843_add_unique_index_to_enterprise_permalink.rb new file mode 100644 index 0000000000..e8841b2c5f --- /dev/null +++ b/db/migrate/20150603001843_add_unique_index_to_enterprise_permalink.rb @@ -0,0 +1,16 @@ +class AddUniqueIndexToEnterprisePermalink < ActiveRecord::Migration + def change + duplicates = Enterprise.group(:permalink).having('count(*) > 1').pluck(:permalink) + duplicates.each { |p| resolve_permalink(p) }; + add_index :enterprises, :permalink, :unique => true + end + + def resolve_permalink(permalink) + conflicting = Enterprise.where(permalink: permalink) + while conflicting.size > 1 do + enterprise = conflicting.pop + enterprise.permalink = nil + enterprise.save + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 6d9de020f1..bf6ecae939 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150527004427) do +ActiveRecord::Schema.define(:version => 20150603001843) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -323,6 +323,7 @@ ActiveRecord::Schema.define(:version => 20150527004427) do add_index "enterprises", ["confirmation_token"], :name => "index_enterprises_on_confirmation_token", :unique => true add_index "enterprises", ["is_primary_producer", "sells"], :name => "index_enterprises_on_is_primary_producer_and_sells" add_index "enterprises", ["owner_id"], :name => "index_enterprises_on_owner_id" + add_index "enterprises", ["permalink"], :name => "index_enterprises_on_permalink", :unique => true add_index "enterprises", ["sells"], :name => "index_enterprises_on_sells" create_table "exchange_fees", :force => true do |t| From 4b6222bbe2ccb20e0ebdb4852e145cf266b2bb3e Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 3 Jun 2015 12:08:47 +1000 Subject: [PATCH 21/30] Updating spec using new button label --- spec/features/consumer/registration_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/consumer/registration_spec.rb b/spec/features/consumer/registration_spec.rb index 25a91ae160..27b748cce5 100644 --- a/spec/features/consumer/registration_spec.rb +++ b/spec/features/consumer/registration_spec.rb @@ -45,7 +45,7 @@ feature "Registration", js: true do # Choosing a type expect(page).to have_content 'Last step to add My Awesome Enterprise!' click_link 'producer-panel' - click_button 'Continue' + click_button 'Create Profile' # Enterprise should be created expect(page).to have_content 'Nice one!' From 17d123db63643399a3a24a5135f77e879090927b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 16:28:43 +1000 Subject: [PATCH 22/30] ng-cloak producers page --- app/views/producers/index.html.haml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/producers/index.html.haml b/app/views/producers/index.html.haml index 0e46795701..e3ae9815e2 100644 --- a/app/views/producers/index.html.haml +++ b/app/views/producers/index.html.haml @@ -1,5 +1,6 @@ -= inject_enterprises -.producers.pad-top{"ng-controller" => "EnterprisesCtrl"} += inject_enterprises + +.producers.pad-top{"ng-controller" => "EnterprisesCtrl", "ng-cloak" => true} .row .small-12.columns.pad-top %h1 Find local producers From 0569ef05050b9f0bb8851f51ff7bd2c4f56cb5ba Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 16:36:31 +1000 Subject: [PATCH 23/30] Only record analytics in production. Also record them in admin backend. --- .../layouts/admin/add_analytics.html.haml.deface | 3 +++ app/views/shared/_analytics.html.haml | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 app/overrides/spree/layouts/admin/add_analytics.html.haml.deface diff --git a/app/overrides/spree/layouts/admin/add_analytics.html.haml.deface b/app/overrides/spree/layouts/admin/add_analytics.html.haml.deface new file mode 100644 index 0000000000..548439b60f --- /dev/null +++ b/app/overrides/spree/layouts/admin/add_analytics.html.haml.deface @@ -0,0 +1,3 @@ +/ insert_bottom "[data-hook='admin_footer_scripts']" + += render 'shared/analytics' diff --git a/app/views/shared/_analytics.html.haml b/app/views/shared/_analytics.html.haml index ee9ba69923..16ad08ff5f 100644 --- a/app/views/shared/_analytics.html.haml +++ b/app/views/shared/_analytics.html.haml @@ -1,8 +1,9 @@ -:javascript - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); +- if Rails.env.production? + :javascript + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - ga('create', 'UA-62912229-1', 'auto'); - ga('send', 'pageview'); + ga('create', 'UA-62912229-1', 'auto'); + ga('send', 'pageview'); From 2c0da5e3509ed58b0084e1e539c6082c1c942472 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 16:38:47 +1000 Subject: [PATCH 24/30] Output parallel spec runtime log -> evenly split spec grouping --- .rspec_parallel | 1 + 1 file changed, 1 insertion(+) diff --git a/.rspec_parallel b/.rspec_parallel index 590f731dd1..867e417e06 100644 --- a/.rspec_parallel +++ b/.rspec_parallel @@ -1,3 +1,4 @@ --format progress --format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log +--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log --tag ~performance From f1019e9221c5ba53470e85e097c2f64d02101199 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 3 Jun 2015 16:53:46 +1000 Subject: [PATCH 25/30] Upgrade unicorn --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 09c5f866f3..621c705d49 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -326,7 +326,7 @@ GEM kaminari (0.14.1) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - kgio (2.7.4) + kgio (2.9.3) launchy (2.1.2) addressable (~> 2.3) letter_opener (1.0.0) @@ -416,7 +416,7 @@ GEM rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) - raindrops (0.9.0) + raindrops (0.13.0) rake (10.4.2) ransack (0.7.2) actionpack (~> 3.0) @@ -503,7 +503,7 @@ GEM uglifier (1.2.4) execjs (>= 0.3.0) multi_json (>= 1.0.2) - unicorn (4.3.1) + unicorn (4.9.0) kgio (~> 2.6) rack raindrops (~> 0.7) From 85d1d67daccb841280b87ab08e84a84da4efd7bd Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 4 Jun 2015 09:42:03 +1000 Subject: [PATCH 26/30] Sort orders as required by spec, fixes intermittent fail --- app/controllers/spree/admin/orders_controller_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb index f7d674048e..e5f831a41c 100644 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ b/app/controllers/spree/admin/orders_controller_decorator.rb @@ -47,7 +47,7 @@ Spree::Admin::OrdersController.class_eval do def managed permissions = OpenFoodNetwork::Permissions.new(spree_current_user) - @orders = permissions.editable_orders.ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) + @orders = permissions.editable_orders.order('id ASC').ransack(params[:q]).result.page(params[:page]).per(params[:per_page]) render json: @orders, each_serializer: Api::Admin::OrderSerializer end end From 552bbf221c0c799c07fe80e0bc5901c625c68dcd Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 5 Jun 2015 10:59:39 +1000 Subject: [PATCH 27/30] Delete obsolete paragraph in registration #595 --- app/views/enterprise_mailer/welcome.html.haml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/views/enterprise_mailer/welcome.html.haml b/app/views/enterprise_mailer/welcome.html.haml index d24cc25904..3cd9d14034 100644 --- a/app/views/enterprise_mailer/welcome.html.haml +++ b/app/views/enterprise_mailer/welcome.html.haml @@ -19,11 +19,6 @@ We also have an online forum for community discussion related to OFN software and the unique challenges of running a food enterprise. You are encouraged to join in. We are constantly evolving and your input into this forum will shape what happens next. = link_to 'Join the community.', 'http://community.openfoodnetwork.org/' -%p - Please find below all the details for viewing and editing your enterprise on - %strong= "#{ Spree::Config.site_name }." - We suggest keeping this email and information somewhere safe. Logging in with the account details below will allow complete access to your products and services. - %p If you have any difficulties, check out our FAQs, browse the forum or post a 'Support' topic and someone will help you out! From e35b39c7cfbbe6750acb7b152ed7b85bac3790c1 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 5 Jun 2015 12:43:49 +1000 Subject: [PATCH 28/30] Handle invalid referer URLs Rescues URI::InvalidURIError of URL(request.referer). --- .../admin/enterprises_controller.rb | 5 +++- app/controllers/application_controller.rb | 5 +++- .../admin/products_controller_decorator.rb | 4 +++- lib/open_food_network/referer_parser.rb | 17 ++++++++++++++ .../open_food_network/referer_parser_spec.rb | 23 +++++++++++++++++++ 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 lib/open_food_network/referer_parser.rb create mode 100644 spec/lib/open_food_network/referer_parser_spec.rb diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index a0b55ad3c8..e50779c3ff 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -1,3 +1,5 @@ +require 'open_food_network/referer_parser' + module Admin class EnterprisesController < ResourceController before_filter :load_enterprise_set, :only => :index @@ -199,7 +201,8 @@ module Admin # Overriding method on Spree's resource controller def location_after_save - refered_from_edit = URI(request.referer).path == main_app.edit_admin_enterprise_path(@enterprise) + referer_path = OpenFoodNetwork::RefererParser::path(request.referer) + refered_from_edit = referer_path == main_app.edit_admin_enterprise_path(@enterprise) if params[:enterprise].key?(:producer_properties_attributes) && !refered_from_edit main_app.admin_enterprises_path else diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 53763ad274..aaa7d0bb06 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,3 +1,5 @@ +require 'open_food_network/referer_parser' + class ApplicationController < ActionController::Base protect_from_forgery @@ -9,7 +11,8 @@ class ApplicationController < ActionController::Base end def set_checkout_redirect - if request.referer and referer_path = URI(request.referer).path + referer_path = OpenFoodNetwork::RefererParser::path(request.referer) + if referer_path session["spree_user_return_to"] = [main_app.checkout_path].include?(referer_path) ? referer_path : root_path end end diff --git a/app/controllers/spree/admin/products_controller_decorator.rb b/app/controllers/spree/admin/products_controller_decorator.rb index 1591586f76..5b1bb347a1 100644 --- a/app/controllers/spree/admin/products_controller_decorator.rb +++ b/app/controllers/spree/admin/products_controller_decorator.rb @@ -1,4 +1,5 @@ require 'open_food_network/spree_api_key_loader' +require 'open_food_network/referer_parser' Spree::Admin::ProductsController.class_eval do include OpenFoodNetwork::SpreeApiKeyLoader @@ -53,7 +54,8 @@ Spree::Admin::ProductsController.class_eval do protected def location_after_save - if URI(request.referer).path == '/admin/products/bulk_edit' + referer_path = OpenFoodNetwork::RefererParser::path(request.referer) + if referer_path == '/admin/products/bulk_edit' bulk_edit_admin_products_url else location_after_save_original diff --git a/lib/open_food_network/referer_parser.rb b/lib/open_food_network/referer_parser.rb new file mode 100644 index 0000000000..b90ef21829 --- /dev/null +++ b/lib/open_food_network/referer_parser.rb @@ -0,0 +1,17 @@ +module OpenFoodNetwork + class RefererParser + def self.path(referer) + parse_uri(referer).andand.path if referer + end + + def self.parse_uri(string) + begin + # TODO: make this operation obsolete by fixing URLs generated by AngularJS + string.sub!('##', '#') + URI(string) + rescue URI::InvalidURIError + nil + end + end + end +end diff --git a/spec/lib/open_food_network/referer_parser_spec.rb b/spec/lib/open_food_network/referer_parser_spec.rb new file mode 100644 index 0000000000..13cde6099e --- /dev/null +++ b/spec/lib/open_food_network/referer_parser_spec.rb @@ -0,0 +1,23 @@ +require 'open_food_network/referer_parser' +require 'spec_helper' + +module OpenFoodNetwork + describe RefererParser do + + it "handles requests without referer" do + RefererParser.path(nil).should be_nil + end + + it "handles requests with referer" do + RefererParser.path('http://example.org/').should eq('/') + end + + it "handles requests with invalid referer" do + RefererParser.path('this is not a URI').should be_nil + end + + it "handles requests with known issue of referer" do + RefererParser.path('http://example.org/##invalid-fragment').should eq('/') + end + end +end From 41dafce0db5ef155fd8fd25c34ffbec0159df88a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 4 Jun 2015 16:04:49 +1000 Subject: [PATCH 29/30] Remove obsolete Bugsnag notification --- app/models/spree/user_decorator.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index d8ea312e23..13ab56c129 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -1,9 +1,7 @@ Spree.user_class.class_eval do - if method_defined? :send_reset_password_instructions_with_delay - Bugsnag.notify RuntimeError.new "send_reset_password_instructions already handled asyncronously - double-calling results in infinite job loop" - else - handle_asynchronously :send_reset_password_instructions - end + # handle_asynchronously will define send_reset_password_instructions_with_delay. + # If handle_asynchronously is called twice, we get an infinite job loop. + handle_asynchronously :send_reset_password_instructions unless method_defined? :send_reset_password_instructions_with_delay has_many :enterprise_roles, :dependent => :destroy has_many :enterprises, through: :enterprise_roles From d6c630dad91df4e2072570c13dbd05ba81407481 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 5 Jun 2015 14:25:45 +1000 Subject: [PATCH 30/30] add spec_helper to customers_controller_spec --- spec/controllers/admin/customers_controller_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/controllers/admin/customers_controller_spec.rb b/spec/controllers/admin/customers_controller_spec.rb index 3fc5451b8b..bb2e4888c2 100644 --- a/spec/controllers/admin/customers_controller_spec.rb +++ b/spec/controllers/admin/customers_controller_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe Admin::CustomersController, type: :controller do include AuthenticationWorkflow