Merge branch 'master' into optimise-shopfront

This commit is contained in:
Rohan Mitchell
2015-06-10 16:39:02 +10:00
38 changed files with 518 additions and 96 deletions

View File

@@ -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

View File

@@ -328,7 +328,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)
@@ -418,7 +418,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)
@@ -505,7 +505,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)

View File

@@ -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()" } } &times;
.small-12.large-8.columns

View File

@@ -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 &gt;

View File

@@ -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

View File

@@ -38,9 +38,13 @@
%i.ofn-i_013-help
&nbsp;
%p Producers make yummy things to eat &amp;/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
&nbsp;
%p If youre not a producer, youre 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" }

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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,22 @@ 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
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
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
@@ -716,7 +732,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 }

View File

@@ -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

View File

@@ -24,6 +24,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

View File

@@ -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

View File

@@ -0,0 +1,3 @@
/ insert_bottom "[data-hook='admin_footer_scripts']"
= render 'shared/analytics'

View File

@@ -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 &nbsp;
%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 &nbsp;
%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'

View File

@@ -1,67 +1,27 @@
%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
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.
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 &nbsp;
-# %p.callout
-# %strong
-# Your enterprise details
-# %table{:width => "100%"}
-# %tr
-# %td{:align => "right"}
-# %strong
-# Shop URL
-# %td &nbsp;
-# %td
-# %a{:href => "#{ main_app.enterprise_shop_url(@enterprise) }", :target => "_blank"}
-# = main_app.enterprise_shop_url(@enterprise)
-# %tr
-# %td &nbsp;
-# %tr
-# %td{:align => "right"}
-# %strong
-# Email
-# %td &nbsp;
-# %td
-# %a{:href => "mailto:#{ @enterprise.email }", :target => "_blank"}
-# = @enterprise.email
%p &nbsp;
%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!
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.callout
%strong
OFN Admin
%table{ :width => "100%"}
%tr
%td{:align => "right"}
%strong
Admin
%td &nbsp;
%td
%a{:href => "#{ spree.admin_url }", :target => "_blank"}
= spree.admin_url
%p &nbsp;
/ /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.
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
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'
= render 'shared/mailers/social_and_contact'

View File

@@ -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

View File

@@ -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');

View File

@@ -0,0 +1,44 @@
= 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]
.row
.four.columns.alpha= label_tag :invoice_date, "Invoice date:"
.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, params[:due_date], class: 'datetimepicker'
.row
.four.columns.alpha= label_tag :account_code, "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
.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)

View File

@@ -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

View File

@@ -21,7 +21,9 @@
%hr/
%p &nbsp;
%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

View File

@@ -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

View File

@@ -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

View File

@@ -136,6 +136,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

View File

View File

@@ -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

View File

@@ -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|

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,104 @@
module OpenFoodNetwork
class XeroInvoicesReport
def initialize(orders, opts={})
@orders = orders
@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
%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
rows = []
@orders.each_with_index do |order, i|
invoice_number = invoice_number_for(order, i)
rows += rows_for_order(order, invoice_number, @opts)
end
rows
end
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),
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)
].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,
order.bill_address.address2,
'',
'',
order.bill_address.city,
order.bill_address.state,
order.bill_address.zipcode,
order.bill_address.country.andand.name,
invoice_number,
order.number,
opts[:invoice_date],
opts[:due_date],
'',
description,
'1',
amount,
'',
opts[:account_code],
tax_type,
'',
'',
'',
'',
Spree::Config.currency,
'',
order.paid? ? 'Y' : 'N'
]
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

View File

@@ -1,3 +1,5 @@
require 'spec_helper'
describe Admin::CustomersController, type: :controller do
include AuthenticationWorkflow

View File

@@ -300,4 +300,94 @@ 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_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 }
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", 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) }
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
xero_invoice_table.should match_table [
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'),
xero_invoice_row('Total taxable fees (tax inclusive)', 20.0, 'GST on Income'),
xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'GST on Income')
]
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 [
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),
xero_invoice_row('Total taxable fees (tax inclusive)', 20.0, 'GST on Income', opts),
xero_invoice_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'GST on Income', opts)
]
end
private
def xero_invoice_table
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, '', 'N']
end
end
end

View File

@@ -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!'

View File

@@ -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

View File

@@ -0,0 +1,37 @@
require 'open_food_network/xero_invoices_report'
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') }
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

View File

@@ -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) }

View File

@@ -24,6 +24,22 @@ module Spree
LineItem.supplied_by_any([s2]).should == [li2]
LineItem.supplied_by_any([s1, s2]).should match_array [li1, li2]
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

View File

@@ -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