Merge branch 'master' into folklabs-producer-emails

This commit is contained in:
Rohan Mitchell
2015-11-11 14:20:25 +11:00
24 changed files with 381 additions and 103 deletions

View File

@@ -1 +1 @@
1.9.3-p392
2.1.5

View File

@@ -3,7 +3,7 @@ sudo: false
cache: bundler
bundler_args: --without development
rvm:
- "1.9.3"
- "2.1.5"
# The test cases are roughly split according to their test times.
# It would be better to use https://github.com/ArturT/knapsack.

View File

@@ -1,5 +1,5 @@
source 'https://rubygems.org'
ruby "1.9.3"
ruby "2.1.5"
gem 'rails', '3.2.21'
gem 'rails-i18n', '~> 3.0.0'
@@ -116,7 +116,7 @@ group :test do
end
group :development do
gem 'pry-debugger'
gem 'pry-byebug'
gem 'debugger-linecache'
gem 'guard'
gem 'guard-livereload'

View File

@@ -171,6 +171,9 @@ GEM
httparty (>= 0.6, < 1.0)
multi_json (~> 1.0)
builder (3.0.4)
byebug (2.7.0)
columnize (~> 0.3)
debugger-linecache (~> 1.2)
cancan (1.6.8)
capybara (2.2.1)
mime-types (>= 1.16)
@@ -196,7 +199,7 @@ GEM
execjs
coffee-script-source (1.3.3)
colorize (0.7.7)
columnize (0.3.6)
columnize (0.9.0)
comfortable_mexican_sofa (1.6.24)
active_link_to (~> 1.0.0)
paperclip (>= 2.3.0)
@@ -230,12 +233,7 @@ GEM
activerecord (~> 3.0)
fog (~> 1.0)
rails (~> 3.0)
debugger (1.6.1)
columnize (>= 0.3.1)
debugger-linecache (~> 1.2.0)
debugger-ruby_core_source (~> 1.2.3)
debugger-linecache (1.2.0)
debugger-ruby_core_source (1.2.3)
delayed_job (4.0.4)
activesupport (>= 3.0, < 4.2)
delayed_job_active_record (4.0.2)
@@ -253,7 +251,7 @@ GEM
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.5.3)
erubis (2.7.0)
eventmachine (1.0.3)
eventmachine (1.0.8)
excon (0.25.3)
execjs (2.5.2)
factory_girl (3.3.0)
@@ -336,7 +334,7 @@ GEM
addressable (~> 2.3)
letter_opener (1.0.0)
launchy (>= 2.0.4)
libv8 (3.16.14.3)
libv8 (3.16.14.11)
listen (2.2.0)
celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3)
@@ -391,9 +389,9 @@ GEM
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.4)
pry-debugger (0.2.2)
debugger (~> 1.3)
pry (~> 0.9.10)
pry-byebug (1.3.2)
byebug (~> 2.7)
pry (~> 0.9.12)
rabl (0.7.2)
activesupport (>= 2.3.14)
multi_json (~> 1.0)
@@ -601,7 +599,7 @@ DEPENDENCIES
parallel_tests
pg
poltergeist
pry-debugger
pry-byebug
rabl
rack-livereload
rack-ssl

View File

@@ -20,7 +20,7 @@ Below are instructions for setting up a development environment for Open Food Ne
## Dependencies
* Rails 3.2.x
* Ruby 1.9.3
* Ruby 2.1.5
* PostgreSQL database
* PhantomJS (for testing)
* See Gemfile for a list of gems required
@@ -44,7 +44,7 @@ You can download the source with the command:
For those new to Rails, the following tutorial will help get you up to speed with configuring a Rails environment: http://guides.rubyonrails.org/getting_started.html .
First, check your dependencies: Ensure that you have Ruby >= 1.9.3 installed:
First, check your dependencies: Ensure that you have Ruby 2.1.5 installed:
ruby --version

View File

@@ -0,0 +1,44 @@
module Spree
module Admin
AdjustmentsController.class_eval do
before_filter :set_included_tax, only: [:create, :update]
before_filter :set_default_tax_rate, only: :edit
private
# Choose a default tax rate to show on the edit form. The adjustment stores its included
# tax in dollars, but doesn't store the source of the tax (ie. TaxRate that generated it).
# We guess which tax rate here, choosing:
# 1. A tax rate that will compute to the same amount as the existing tax
# 2. If that's not present, the first tax rate that's valid for the current order
# When we have to go with 2, we show an error message to ask the admin to check that the
# correct tax is being applied.
def set_default_tax_rate
if @adjustment.included_tax > 0
trs = TaxRate.match(@order)
tr_yielding_matching_tax = trs.select { |tr| tr.compute_tax(@adjustment.amount) == @adjustment.included_tax }.first.andand.id
tr_valid_for_order = TaxRate.match(@order).first.andand.id
@tax_rate_id = tr_yielding_matching_tax || tr_valid_for_order
if tr_yielding_matching_tax.nil?
@adjustment.errors.add :tax_rate_id, "^Please check that the tax rate for this adjustment is correct."
end
end
end
def set_included_tax
if params[:tax_rate_id].present?
tax_rate = TaxRate.find params[:tax_rate_id]
amount = params[:adjustment][:amount].to_f
params[:adjustment][:included_tax] = tax_rate.compute_tax amount
else
params[:adjustment][:included_tax] = 0
end
end
end
end
end

View File

@@ -24,6 +24,10 @@ module Spree
update_attributes! included_tax: tax.round(2)
end
def display_included_tax
Spree::Money.new(included_tax, { :currency => currency })
end
def has_tax?
included_tax > 0
end

View File

@@ -1,20 +1,61 @@
Spree::TaxRate.class_eval do
class << self
def match_with_sales_tax_registration(order)
return [] if order.distributor && !order.distributor.charges_sales_tax
match_without_sales_tax_registration(order)
module Spree
TaxRate.class_eval do
class << self
def match_with_sales_tax_registration(order)
return [] if order.distributor && !order.distributor.charges_sales_tax
match_without_sales_tax_registration(order)
end
alias_method_chain :match, :sales_tax_registration
end
alias_method_chain :match, :sales_tax_registration
end
def adjust_with_included_tax(order)
adjust_without_included_tax(order)
def adjust_with_included_tax(order)
adjust_without_included_tax(order)
order.reload
(order.adjustments.tax + order.price_adjustments).each do |a|
a.set_absolute_included_tax! a.amount
order.reload
(order.adjustments.tax + order.price_adjustments).each do |a|
a.set_absolute_included_tax! a.amount
end
end
alias_method_chain :adjust, :included_tax
# Manually apply a TaxRate to a particular amount. TaxRates normally compute against
# LineItems or Orders, so we mock out a line item here to fit the interface
# that our calculator (usually DefaultTax) expects.
def compute_tax(amount)
product = OpenStruct.new tax_category: tax_category
line_item = LineItem.new quantity: 1
line_item.define_singleton_method(:product) { product }
line_item.define_singleton_method(:price) { amount }
# Tax on adjustments (represented by the included_tax field) is always inclusive of
# tax. However, there's nothing to stop an admin from setting one up with a tax rate
# that's marked as not inclusive of tax, and that would result in the DefaultTax
# calculator generating a slightly incorrect value. Therefore, we treat the tax
# rate as inclusive of tax for the calculations below, regardless of its original
# setting.
with_tax_included_in_price do
calculator.compute line_item
end
end
private
def with_tax_included_in_price
old_included_in_price = self.included_in_price
self.included_in_price = true
calculator.calculable.included_in_price = true
result = yield
ensure
self.included_in_price = old_included_in_price
calculator.calculable.included_in_price = old_included_in_price
result
end
end
alias_method_chain :adjust, :included_tax
end

View File

@@ -0,0 +1,9 @@
/ replace_contents "[data-hook='adjustment_row']"
%td.align-center.created_at= pretty_time(adjustment.created_at)
%td.align-center.label= adjustment.label
%td.align-center.amount= adjustment.display_amount.to_html
%td.align-center.included-tax= adjustment.display_included_tax.to_html
%td.actions
= link_to_edit adjustment, no_text: true
= link_to_delete adjustment, no_text: true

View File

@@ -0,0 +1,8 @@
/ replace_contents "[data-hook='adjustmment_head']"
%tr
%th= "#{t('spree.date')}/#{t('spree.time')}"
%th= t(:description)
%th= t(:amount)
%th= t(:included_tax)
%th.actions

View File

@@ -0,0 +1,6 @@
/ replace_contents "[data-hook='admin_adjustment_form_fields']"
- if @adjustment.new_record?
= render 'new_form', f: f
- else
= render 'edit_form', f: f

View File

@@ -1,2 +1,2 @@
object OpenStruct.new(flash)
object OpenStruct.new(flash.to_hash)
attributes :info, :success, :error

View File

@@ -0,0 +1,25 @@
.row
.alpha.four.columns
= f.field_container :amount do
= f.label :amount, raw(t(:amount) + content_tag(:span, " *", :class => "required"))
= text_field :adjustment, :amount, :class => 'fullwidth'
= f.error_message_on :amount
.four.columns
= f.field_container :included_tax do
= f.label :included_tax, t(:included_tax)
= text_field :adjustment, :included_tax, disabled: true, class: 'fullwidth'
= f.error_message_on :included_tax
.omega.four.columns
= f.field_container :tax_rate_id do
= f.label :tax_rate_id, t(:tax_rate)
= select_tag :tax_rate_id, options_from_collection_for_select(Spree::TaxRate.all, :id, :name, @tax_rate_id), prompt: t(:remove_tax), class: 'select2 fullwidth'
= f.error_message_on :tax_rate_id
.row
.alpha.omega.twelve.columns
= f.field_container :label do
= f.label :label, raw(t(:description) + content_tag(:span, " *", :class => "required"))
= text_field :adjustment, :label, :class => 'fullwidth'
= f.error_message_on :label

View File

@@ -0,0 +1,19 @@
.row
.alpha.three.columns
= f.field_container :amount do
= f.label :amount, raw(t(:amount) + content_tag(:span, " *", :class => "required"))
= text_field :adjustment, :amount, :class => 'fullwidth'
= f.error_message_on :amount
.omega.three.columns
= f.field_container :tax_rate_id do
= f.label :tax_rate_id, t(:tax_rate)
= select_tag :tax_rate_id, options_from_collection_for_select(Spree::TaxRate.all, :id, :name), prompt: t(:none), class: 'select2 fullwidth'
= f.error_message_on :tax_rate_id
.row
.alpha.omega.twelve.columns
= f.field_container :label do
= f.label :label, raw(t(:description) + content_tag(:span, " *", :class => "required"))
= text_field :adjustment, :label, :class => 'fullwidth'
= f.error_message_on :label

View File

@@ -38,3 +38,5 @@ en:
footer_about_url: "About URL"
footer_tos_url: "Terms of Service URL"
invoice: "Invoice"
included_tax: "Included tax"
remove_tax: "Remove tax"

View File

@@ -35,43 +35,8 @@ module OpenFoodNetwork
tax_rates = enterprise_fee.tax_category ? enterprise_fee.tax_category.tax_rates.match(order) : []
tax_rates.sum do |rate|
compute_tax rate, adjustment.amount
rate.compute_tax adjustment.amount
end
end
# Apply a TaxRate to a particular amount. TaxRates normally compute against
# LineItems or Orders, so we mock out a line item here to fit the interface
# that our calculator (usually DefaultTax) expects.
def compute_tax(tax_rate, amount)
product = OpenStruct.new tax_category: tax_rate.tax_category
line_item = Spree::LineItem.new quantity: 1
line_item.define_singleton_method(:product) { product }
line_item.define_singleton_method(:price) { amount }
# The enterprise fee adjustments for which we're calculating tax are always inclusive of
# tax. However, there's nothing to stop an admin from setting one up with a tax rate
# that's marked as not inclusive of tax, and that would result in the DefaultTax
# calculator generating a slightly incorrect value. Therefore, we treat the tax
# rate as inclusive of tax for the calculations below, regardless of its original
# setting.
with_tax_included_in_price(tax_rate) do
tax_rate.calculator.compute line_item
end
end
def with_tax_included_in_price(tax_rate)
old_included_in_price = tax_rate.included_in_price
tax_rate.included_in_price = true
tax_rate.calculator.calculable.included_in_price = true
result = yield
tax_rate.included_in_price = old_included_in_price
tax_rate.calculator.calculable.included_in_price = old_included_in_price
result
end
end
end

View File

@@ -1,5 +1,5 @@
function load_environment {
source /var/lib/jenkins/.rvm/environments/ruby-1.9.3-p392
source /var/lib/jenkins/.rvm/environments/ruby-2.1.5
if [ ! -f config/application.yml ]; then
ln -s application.yml.example config/application.yml
fi

View File

@@ -0,0 +1,60 @@
require 'spec_helper'
module Spree
describe Admin::AdjustmentsController do
include AuthenticationWorkflow
before { login_as_admin }
describe "setting included tax" do
let(:order) { create(:order) }
let(:tax_rate) { create(:tax_rate, amount: 0.1, calculator: Spree::Calculator::DefaultTax.new) }
describe "creating an adjustment" do
it "sets included tax to zero when no tax rate is specified" do
spree_post :create, {order_id: order.number, adjustment: {label: 'Testing included tax', amount: '110'}, tax_rate_id: ''}
response.should redirect_to spree.admin_order_adjustments_path(order)
a = Adjustment.last
a.label.should == 'Testing included tax'
a.amount.should == 110
a.included_tax.should == 0
end
it "calculates included tax when a tax rate is provided" do
spree_post :create, {order_id: order.number, adjustment: {label: 'Testing included tax', amount: '110'}, tax_rate_id: tax_rate.id.to_s}
response.should redirect_to spree.admin_order_adjustments_path(order)
a = Adjustment.last
a.label.should == 'Testing included tax'
a.amount.should == 110
a.included_tax.should == 10
end
end
describe "updating an adjustment" do
let(:adjustment) { create(:adjustment, adjustable: order, amount: 1100, included_tax: 100) }
it "sets included tax to zero when no tax rate is specified" do
spree_put :update, {order_id: order.number, id: adjustment.id, adjustment: {label: 'Testing included tax', amount: '110'}, tax_rate_id: ''}
response.should redirect_to spree.admin_order_adjustments_path(order)
a = Adjustment.last
a.label.should == 'Testing included tax'
a.amount.should == 110
a.included_tax.should == 0
end
it "calculates included tax when a tax rate is provided" do
spree_put :update, {order_id: order.number, id: adjustment.id, adjustment: {label: 'Testing included tax', amount: '110'}, tax_rate_id: tax_rate.id.to_s}
response.should redirect_to spree.admin_order_adjustments_path(order)
a = Adjustment.last
a.label.should == 'Testing included tax'
a.amount.should == 110
a.included_tax.should == 10
end
end
end
end
end

View File

@@ -0,0 +1,89 @@
require "spec_helper"
feature %q{
As an administrator
I want to manage adjustments on orders
} do
include AuthenticationWorkflow
include WebHelper
let!(:user) { create(:user) }
let!(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) }
let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) }
let!(:order) { create(:order_with_totals_and_distribution, user: user, distributor: distributor, order_cycle: order_cycle, state: 'complete', payment_state: 'balance_due') }
let!(:tax_rate) { create(:tax_rate, name: 'GST', calculator: build(:calculator, preferred_amount: 10), zone: create(:zone_with_member)) }
before do
order.finalize!
create(:check_payment, order: order, amount: order.total)
end
scenario "adding taxed adjustments to an order" do
# When I go to the adjustments page for the order
login_to_admin_section
visit spree.admin_orders_path
page.find('td.actions a.icon-edit').click
click_link 'Adjustments'
# And I create a new adjustment with tax
click_link 'New Adjustment'
fill_in 'adjustment_amount', with: 110
fill_in 'adjustment_label', with: 'Late fee'
select 'GST', from: 'tax_rate_id'
click_button 'Continue'
# Then I should see the adjustment, with the correct tax
page.should have_selector 'td.label', text: 'Late fee'
page.should have_selector 'td.amount', text: '110'
page.should have_selector 'td.included-tax', text: '10'
end
scenario "modifying taxed adjustments on an order" do
# Given a taxed adjustment
adjustment = create(:adjustment, adjustable: order, amount: 110, included_tax: 10)
# When I go to the adjustments page for the order
login_to_admin_section
visit spree.admin_orders_path
page.find('td.actions a.icon-edit').click
click_link 'Adjustments'
page.find('td.actions a.icon-edit').click
# Then I should see the uneditable included tax and our tax rate as the default
page.should have_field :adjustment_included_tax, with: '10.00', disabled: true
page.should have_select :tax_rate_id, selected: 'GST'
# When I edit the adjustment, removing the tax
select 'Remove tax', from: :tax_rate_id
click_button 'Continue'
# Then the adjustment tax should be cleared
page.should have_selector 'td.amount', text: '110'
page.should have_selector 'td.included-tax', text: '0'
end
scenario "modifying an untaxed adjustment on an order" do
# Given an untaxed adjustment
adjustment = create(:adjustment, adjustable: order, amount: 110, included_tax: 0)
# When I go to the adjustments page for the order
login_to_admin_section
visit spree.admin_orders_path
page.find('td.actions a.icon-edit').click
click_link 'Adjustments'
page.find('td.actions a.icon-edit').click
# Then I should see the uneditable included tax and 'Remove tax' as the default tax rate
page.should have_field :adjustment_included_tax, with: '0.00', disabled: true
page.should have_select :tax_rate_id, selected: []
# When I edit the adjustment, setting a tax rate
select 'GST', from: :tax_rate_id
click_button 'Continue'
# Then the adjustment tax should be recalculated
page.should have_selector 'td.amount', text: '110'
page.should have_selector 'td.included-tax', text: '10'
end
end

View File

@@ -294,7 +294,6 @@ feature %q{
expect(page).to have_selector "tr#li_#{li1.id}"
expect(page).to have_selector "tr#li_#{li2.id}"
select2_select oc1.name, from: "order_cycle_filter"
expect(page).to have_selector "#loading img.spinner"
expect(page).to_not have_selector "#loading img.spinner"
expect(page).to have_selector "tr#li_#{li1.id}"
expect(page).to_not have_selector "tr#li_#{li2.id}"

View File

@@ -1,8 +1,8 @@
require "spec_helper"
feature %q{
As a payment administrator
I want to capture multiple payments quickly from the one page
As an administrator
I want to manage orders
} do
include AuthenticationWorkflow
include WebHelper
@@ -10,7 +10,7 @@ feature %q{
background do
@user = create(:user)
@product = create(:simple_product)
@distributor = create(:distributor_enterprise)
@distributor = create(:distributor_enterprise, charges_sales_tax: true)
@order_cycle = create(:simple_order_cycle, distributors: [@distributor], variants: [@product.variants.first])
@order = create(:order_with_totals_and_distribution, user: @user, distributor: @distributor, order_cycle: @order_cycle, state: 'complete', payment_state: 'balance_due')
@@ -106,6 +106,7 @@ feature %q{
current_path.should == spree.admin_orders_path
end
context "as an enterprise manager" do
let(:coordinator1) { create(:distributor_enterprise) }
let(:coordinator2) { create(:distributor_enterprise) }

View File

@@ -65,33 +65,4 @@ module OpenFoodNetwork
efa.send(:order_adjustment_label).should == "Whole order - packing fee by distributor Ballantyne"
end
end
describe "ensuring that tax rate is marked as tax included_in_price" do
let(:efa) { EnterpriseFeeApplicator.new nil, nil, nil }
let(:tax_rate) { create(:tax_rate, included_in_price: false, calculator: Spree::Calculator::DefaultTax.new) }
it "sets included_in_price to true" do
efa.send(:with_tax_included_in_price, tax_rate) do
tax_rate.included_in_price.should be_true
end
end
it "sets the included_in_price value accessible to the calculator to true" do
efa.send(:with_tax_included_in_price, tax_rate) do
tax_rate.calculator.calculable.included_in_price.should be_true
end
end
it "passes through the return value of the block" do
efa.send(:with_tax_included_in_price, tax_rate) do
'asdf'
end.should == 'asdf'
end
it "restores both values to their original afterwards" do
efa.send(:with_tax_included_in_price, tax_rate) {}
tax_rate.included_in_price.should be_false
tax_rate.calculator.calculable.included_in_price.should be_false
end
end
end

View File

@@ -29,5 +29,42 @@ module Spree
end
end
end
describe "ensuring that tax rate is marked as tax included_in_price" do
let(:tax_rate) { create(:tax_rate, included_in_price: false, calculator: Spree::Calculator::DefaultTax.new) }
it "sets included_in_price to true" do
tax_rate.send(:with_tax_included_in_price) do
tax_rate.included_in_price.should be_true
end
end
it "sets the included_in_price value accessible to the calculator to true" do
tax_rate.send(:with_tax_included_in_price) do
tax_rate.calculator.calculable.included_in_price.should be_true
end
end
it "passes through the return value of the block" do
tax_rate.send(:with_tax_included_in_price) do
'asdf'
end.should == 'asdf'
end
it "restores both values to their original afterwards" do
tax_rate.send(:with_tax_included_in_price) {}
tax_rate.included_in_price.should be_false
tax_rate.calculator.calculable.included_in_price.should be_false
end
it "restores both values when an exception is raised" do
expect do
tax_rate.send(:with_tax_included_in_price) { raise Exception.new 'oops' }
end.to raise_error 'oops'
tax_rate.included_in_price.should be_false
tax_rate.calculator.calculable.included_in_price.should be_false
end
end
end
end

View File

@@ -1,6 +1,6 @@
module OpenFoodNetwork
module HtmlHelper
def save_and_open(html)
def html_save_and_open(html)
require "launchy"
file = Tempfile.new('html')
file.write html