mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge branch 'master' into optimise-shopfront
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 >
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
/ insert_bottom "[data-hook='admin_footer_scripts']"
|
||||
|
||||
= render 'shared/analytics'
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
-# %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!
|
||||
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
|
||||
%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.
|
||||
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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
44
app/views/spree/admin/reports/xero_invoices.html.haml
Normal file
44
app/views/spree/admin/reports/xero_invoices.html.haml
Normal 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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
0
db/migrate/20120327000593_add_addresses_checkouts_indexes.rb
Executable file → Normal file
0
db/migrate/20120327000593_add_addresses_checkouts_indexes.rb
Executable file → Normal 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
|
||||
@@ -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|
|
||||
|
||||
@@ -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
|
||||
|
||||
17
lib/open_food_network/referer_parser.rb
Normal file
17
lib/open_food_network/referer_parser.rb
Normal 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
|
||||
104
lib/open_food_network/xero_invoices_report.rb
Normal file
104
lib/open_food_network/xero_invoices_report.rb
Normal 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
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Admin::CustomersController, type: :controller do
|
||||
include AuthenticationWorkflow
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!'
|
||||
|
||||
23
spec/lib/open_food_network/referer_parser_spec.rb
Normal file
23
spec/lib/open_food_network/referer_parser_spec.rb
Normal 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
|
||||
37
spec/lib/open_food_network/xero_invoices_report_spec.rb
Normal file
37
spec/lib/open_food_network/xero_invoices_report_spec.rb
Normal 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
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user