diff --git a/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee b/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee
index d4f544e300..06ee4fa4ef 100644
--- a/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee
+++ b/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee
@@ -1 +1 @@
-angular.module("admin.accounts_and_billing_settings", [])
+angular.module("admin.accounts_and_billing_settings", ["admin.utils"])
diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js
index 30c8e1a7b9..c0fa530626 100644
--- a/app/assets/javascripts/admin/all.js
+++ b/app/assets/javascripts/admin/all.js
@@ -23,6 +23,7 @@
//= require_tree ../templates/admin
//= require ./admin
//= require ./accounts_and_billing_settings/accounts_and_billing_settings
+//= require ./business_model_configuration/business_model_configuration
//= require ./customers/customers
//= require ./dropdown/dropdown
//= require ./enterprises/enterprises
diff --git a/app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee b/app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee
new file mode 100644
index 0000000000..cecb7c397e
--- /dev/null
+++ b/app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee
@@ -0,0 +1 @@
+angular.module("admin.businessModelConfiguration", ["admin.utils"])
diff --git a/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee b/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee
new file mode 100644
index 0000000000..ca757c673d
--- /dev/null
+++ b/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee
@@ -0,0 +1,21 @@
+angular.module("admin.businessModelConfiguration").controller "BusinessModelConfigCtrl", ($scope, $filter) ->
+ $scope.turnover = 1000
+
+ $scope.bill = ->
+ return $filter('currency')(0) unless $scope.fixed || $scope.rate
+ Number($scope.fixed) + Number($scope.turnover) * Number($scope.rate)
+
+ $scope.cappedBill = ->
+ return $scope.bill() if !$scope.cap? || Number($scope.cap) == 0
+ Math.min($scope.bill(), Number($scope.cap))
+
+ $scope.capReached = ->
+ return "No" if !$scope.cap? || Number($scope.cap) == 0
+ if $scope.bill() >= Number($scope.cap) then "Yes" else "No"
+
+ $scope.includedTax = ->
+ return 0 if !$scope.taxRate? || Number($scope.taxRate) == 0
+ ($scope.cappedBill() * Number($scope.taxRate))
+
+ $scope.total = ->
+ $scope.cappedBill() + $scope.includedTax()
diff --git a/app/assets/javascripts/admin/enterprises/directives/monthly_pricing_description.js.coffee b/app/assets/javascripts/admin/enterprises/directives/monthly_pricing_description.js.coffee
new file mode 100644
index 0000000000..6331fa2ca5
--- /dev/null
+++ b/app/assets/javascripts/admin/enterprises/directives/monthly_pricing_description.js.coffee
@@ -0,0 +1,8 @@
+angular.module("admin.enterprises").directive "monthlyPricingDescription", (monthlyBillDescription) ->
+ restrict: 'E'
+ scope:
+ joiner: "@"
+ template: ""
+ link: (scope, element, attrs) ->
+ joiners = { comma: ", ", newline: "
" }
+ scope.billDescription = monthlyBillDescription.replace("{joiner}", joiners[scope.joiner])
diff --git a/app/assets/javascripts/admin/accounts_and_billing_settings/directives/watchValueAs.js.coffee b/app/assets/javascripts/admin/utils/directives/watchValueAs.js.coffee
similarity index 72%
rename from app/assets/javascripts/admin/accounts_and_billing_settings/directives/watchValueAs.js.coffee
rename to app/assets/javascripts/admin/utils/directives/watchValueAs.js.coffee
index a14288db55..701858cd56 100644
--- a/app/assets/javascripts/admin/accounts_and_billing_settings/directives/watchValueAs.js.coffee
+++ b/app/assets/javascripts/admin/utils/directives/watchValueAs.js.coffee
@@ -1,4 +1,4 @@
-angular.module("admin.accounts_and_billing_settings").directive "watchValueAs", ->
+angular.module("admin.utils").directive "watchValueAs", ->
restrict: 'A'
scope: {
value: "=watchValueAs"
diff --git a/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml b/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml
index c42d8910e1..7f143d1071 100644
--- a/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml
+++ b/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml
@@ -15,7 +15,8 @@
%h3 Hub Shop
%p
- %strong COST: 2% OF SALES, CAPPED AT $50 PER MONTH
+ %strong
+ %monthly-pricing-description{ joiner: "comma" }
%p Your enterprise is the backbone of your local food system. You aggregate produce from other enterprises and can sell it through your shop on the Open Food Network.
@@ -53,7 +54,8 @@
%h3 Producer Shop
%p
- %strong COST: 2% OF SALES, CAPPED AT $50 PER MONTH
+ %strong
+ %monthly-pricing-description{ joiner: "comma" }
%p Sell your products directly to customers through your very own Open Food Network shopfront.
@@ -63,7 +65,8 @@
%h3 Producer Hub
%p
- %strong COST: 2% OF SALES, CAPPED AT $50 PER MONTH
+ %strong
+ %monthly-pricing-description{ joiner: "comma" }
%p Your enterprise is the backbone of your local food system. You can sell your own produce as well as produce aggregated from other enterprises through your shopfront on the Open Food Network.
@@ -94,9 +97,7 @@
%h3 Hub Shop
%p Sell produce from others
.bottom
- \2% OF SALES
- %br
- CAPPED AT $50 PER MONTH
+ %monthly-pricing-description{ joiner: "newline" }
%div{ ng: { switch: { when: "true" } } }
%a.button.selector.producer-profile{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } }
@@ -109,17 +110,14 @@
%h3 Producer Shop
%p Sell your own produce
.bottom
- \2% OF SALES
- %br
- CAPPED AT $50 PER MONTH
+ %monthly-pricing-description{ joiner: "newline" }
+
%a.button.selector.producer-hub{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } }
.top
%h3 Producer Hub
%p Sell produce from self and others
.bottom
- \2% OF SALES
- %br
- CAPPED AT $50 PER MONTH
+ %monthly-pricing-description{ joiner: "newline" }
%a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } }
%span{ ng: {hide: "saved() || saving" } }
diff --git a/app/assets/stylesheets/admin/openfoodnetwork.css.scss b/app/assets/stylesheets/admin/openfoodnetwork.css.scss
index c43b4098e7..d5a53e11c4 100644
--- a/app/assets/stylesheets/admin/openfoodnetwork.css.scss
+++ b/app/assets/stylesheets/admin/openfoodnetwork.css.scss
@@ -6,6 +6,10 @@
text-align: right;
}
+.underline {
+ text-decoration: underline;
+}
+
table .blank-action {
display: inline-block;
width: 29px;
diff --git a/app/assets/stylesheets/admin/orders.css.scss b/app/assets/stylesheets/admin/orders.css.scss
index d91bb28934..4676d93b1a 100644
--- a/app/assets/stylesheets/admin/orders.css.scss
+++ b/app/assets/stylesheets/admin/orders.css.scss
@@ -56,3 +56,21 @@ div#group_buy_calculation {
text-align: center;
}
}
+
+.input-symbol {
+ position: relative;
+ &.before {
+
+ span {
+ position: absolute;
+ transform: translate(0,-50%);
+ top:50%;
+ pointer-events:none;
+ margin-left: 1em;
+ }
+
+ input {
+ text-indent:1em;
+ }
+ }
+}
diff --git a/app/controllers/admin/account_controller.rb b/app/controllers/admin/account_controller.rb
index a752b2cfdd..00531cd426 100644
--- a/app/controllers/admin/account_controller.rb
+++ b/app/controllers/admin/account_controller.rb
@@ -2,8 +2,5 @@ class Admin::AccountController < Spree::Admin::BaseController
def show
@invoices = spree_current_user.account_invoices
- # @enterprises = Enterprise.where(id: BillablePeriod.where(owner_id: spree_current_user).map(&:enterprise_id))
- # .group_by('enterprise.id').joins(:billable_periods)
- # .select('SUM(billable_periods.turnover) AS turnover').order('turnover DESC')
end
end
diff --git a/app/controllers/admin/business_model_configuration_controller.rb b/app/controllers/admin/business_model_configuration_controller.rb
new file mode 100644
index 0000000000..312a2e3208
--- /dev/null
+++ b/app/controllers/admin/business_model_configuration_controller.rb
@@ -0,0 +1,31 @@
+require 'open_food_network/business_model_configuration_validator'
+
+class Admin::BusinessModelConfigurationController < Spree::Admin::BaseController
+ before_filter :load_settings, only: [:edit, :update]
+ before_filter :require_valid_settings, only: [:update]
+
+ def update
+ Spree::Config.set(params[:settings])
+ flash[:success] = t(:successfully_updated, :resource => t(:business_model_configuration))
+ redirect_to_edit
+ end
+
+ private
+
+ def redirect_to_edit
+ redirect_to main_app.edit_admin_business_model_configuration_path
+ end
+
+ def load_settings
+ @settings = OpenFoodNetwork::BusinessModelConfigurationValidator.new(params[:settings] || {
+ account_invoices_monthly_fixed: Spree::Config[:account_invoices_monthly_fixed],
+ account_invoices_monthly_rate: Spree::Config[:account_invoices_monthly_rate],
+ account_invoices_monthly_cap: Spree::Config[:account_invoices_monthly_cap],
+ account_invoices_tax_rate: Spree::Config[:account_invoices_tax_rate]
+ })
+ end
+
+ def require_valid_settings
+ render :edit unless @settings.valid?
+ end
+end
diff --git a/app/helpers/admin/account_helper.rb b/app/helpers/admin/account_helper.rb
new file mode 100644
index 0000000000..0292522b74
--- /dev/null
+++ b/app/helpers/admin/account_helper.rb
@@ -0,0 +1,14 @@
+module Admin
+ module AccountHelper
+ def invoice_description_for(invoice)
+ month = t(:abbr_month_names, :scope => :date)[invoice.month]
+ year = invoice.year
+ star = invoice.order.nil? || invoice.order.completed? ? "" : "*"
+ "#{month} #{year}#{star}"
+ end
+
+ def invoice_total_for(invoice)
+ invoice.order.andand.display_total || Spree::Money.new(0, { :currency => Spree::Config[:currency] })
+ end
+ end
+end
diff --git a/app/helpers/admin/business_model_configuration_helper.rb b/app/helpers/admin/business_model_configuration_helper.rb
new file mode 100644
index 0000000000..dcfebb1969
--- /dev/null
+++ b/app/helpers/admin/business_model_configuration_helper.rb
@@ -0,0 +1,48 @@
+module Admin
+ module BusinessModelConfigurationHelper
+ def monthly_bill_description
+ plus = monthly_bill_includes_fixed? && monthly_bill_includes_rate? ? " + " : ""
+
+ if fixed_description.empty? && rate_description.empty?
+ t(:free).upcase
+ elsif monthly_bill_includes_cap? && monthly_bill_includes_rate? # only care about cap if there is a rate too
+ "#{fixed_description}#{plus}#{rate_description}{joiner}#{cap_description} #{t(:per_month).upcase}#{tax_description.upcase}"
+ else
+ "#{fixed_description}#{plus}#{rate_description} #{t(:per_month).upcase}#{tax_description.upcase}"
+ end
+ end
+
+ private
+
+ def fixed_description
+ fixed_amount = Spree::Money.new(Spree::Config[:account_invoices_monthly_fixed], {currency: Spree::Config[:currency]} ).rounded
+ monthly_bill_includes_fixed? ? "#{fixed_amount}" : ""
+ end
+
+ def rate_description
+ percentage = (Spree::Config[:account_invoices_monthly_rate]*100).round(2)
+ monthly_bill_includes_rate? ? t(:percentage_of_sales, percentage: "#{percentage}%").upcase : ""
+ end
+
+ def cap_description
+ cap_amount = Spree::Money.new(Spree::Config[:account_invoices_monthly_cap], { currency: Spree::Config[:currency] }).rounded
+ monthly_bill_includes_cap? ? "#{t(:capped_at_cap, cap: cap_amount).upcase}" : ""
+ end
+
+ def tax_description
+ Spree::Config[:account_invoices_tax_rate] > 0 ? ", #{t(:plus_tax).upcase}" : ""
+ end
+
+ def monthly_bill_includes_fixed?
+ Spree::Config[:account_invoices_monthly_fixed] > 0
+ end
+
+ def monthly_bill_includes_rate?
+ Spree::Config[:account_invoices_monthly_rate] > 0
+ end
+
+ def monthly_bill_includes_cap?
+ Spree::Config[:account_invoices_monthly_cap] > 0
+ end
+ end
+end
diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb
index 490e3dc00b..6036447d9b 100644
--- a/app/helpers/admin/injection_helper.rb
+++ b/app/helpers/admin/injection_helper.rb
@@ -1,5 +1,7 @@
module Admin
module InjectionHelper
+ include BusinessModelConfigurationHelper
+
def admin_inject_enterprise
admin_inject_json_ams "admin.enterprises", "enterprise", @enterprise, Api::Admin::EnterpriseSerializer
end
@@ -78,6 +80,10 @@ module Admin
admin_inject_json_ams_array "admin.orders", "orderCycles", @order_cycles, Api::Admin::BasicOrderCycleSerializer, current_user: spree_current_user
end
+ def admin_inject_monthly_bill_description
+ render partial: "admin/json/injection_ams", locals: {ngModule: "admin.enterprises", name: "monthlyBillDescription", json: monthly_bill_description.to_json}
+ end
+
def admin_inject_spree_api_key
render partial: "admin/json/injection_ams", locals: {ngModule: 'ofn.admin', name: 'SpreeApiKey', json: "'#{@spree_api_key.to_s}'"}
end
diff --git a/app/jobs/finalize_account_invoices.rb b/app/jobs/finalize_account_invoices.rb
index 4baa841db6..25b614bebc 100644
--- a/app/jobs/finalize_account_invoices.rb
+++ b/app/jobs/finalize_account_invoices.rb
@@ -51,7 +51,7 @@ class FinalizeAccountInvoices
job: "FinalizeAccountInvoices",
error: "end_date is in the future",
data: {
- end_date: end_date.localtime.strftime("%F %T"),
+ end_date: end_date.in_time_zone.strftime("%F %T"),
now: Time.zone.now.strftime("%F %T")
}
})
diff --git a/app/jobs/update_account_invoices.rb b/app/jobs/update_account_invoices.rb
index c0597a9ba3..16112ef870 100644
--- a/app/jobs/update_account_invoices.rb
+++ b/app/jobs/update_account_invoices.rb
@@ -32,10 +32,14 @@ class UpdateAccountInvoices
invoice_order: account_invoice.order.as_json
})
else
- billable_periods = account_invoice.billable_periods.order(:enterprise_id, :begins_at).reject{ |bp| bp.turnover == 0 }
+ billable_periods = account_invoice.billable_periods.order(:enterprise_id, :begins_at).reject{ |bp| bp.bill == 0 }
if billable_periods.any?
- address = billable_periods.first.enterprise.address
+ oldest_enterprise = billable_periods.first.enterprise
+ address = oldest_enterprise.address.dup
+ first, space, last = (oldest_enterprise.contact || "").partition(' ')
+ address.update_attributes(phone: oldest_enterprise.phone) if oldest_enterprise.phone.present?
+ address.update_attributes(firstname: first, lastname: last) if first.present? && last.present?
account_invoice.order.update_attributes(bill_address: address, ship_address: address)
end
@@ -81,7 +85,7 @@ class UpdateAccountInvoices
job: "UpdateAccountInvoices",
error: "end_date is in the future",
data: {
- end_date: end_date.localtime.strftime("%F %T"),
+ end_date: end_date.in_time_zone.strftime("%F %T"),
now: Time.zone.now.strftime("%F %T")
}
})
diff --git a/app/jobs/update_billable_periods.rb b/app/jobs/update_billable_periods.rb
index 598d5f8e7b..80f19d961a 100644
--- a/app/jobs/update_billable_periods.rb
+++ b/app/jobs/update_billable_periods.rb
@@ -91,10 +91,10 @@ class UpdateBillablePeriods
def clean_up_untouched_billable_periods_for(enterprise, job_start_time)
# Snag and then delete any BillablePeriods which overlap
- obsolete_billable_periods = enterprise.billable_periods.where('ends_at > (?) AND begins_at < (?) AND updated_at < (?)', start_date, end_date, job_start_time)
+ obsolete_billable_periods = enterprise.billable_periods.where('ends_at > (?) AND begins_at < (?) AND billable_periods.updated_at < (?)', start_date, end_date, job_start_time)
if obsolete_billable_periods.any?
- current_billable_periods = enterprise.billable_periods.where('ends_at >= (?) AND begins_at <= (?) AND updated_at > (?)', start_date, end_date, job_start_time)
+ current_billable_periods = enterprise.billable_periods.where('ends_at >= (?) AND begins_at <= (?) AND billable_periods.updated_at > (?)', start_date, end_date, job_start_time)
Delayed::Worker.logger.info "#{enterprise.name} #{start_date.strftime("%F %T")} #{job_start_time.strftime("%F %T")}"
Delayed::Worker.logger.info "#{obsolete_billable_periods.first.updated_at.strftime("%F %T")}"
@@ -105,7 +105,9 @@ class UpdateBillablePeriods
})
end
- obsolete_billable_periods.each(&:delete)
+ obsolete_billable_periods.includes({ account_invoice: :order}).
+ where('spree_orders.state <> \'complete\' OR account_invoices.order_id IS NULL').
+ each(&:delete)
end
private
@@ -116,7 +118,7 @@ class UpdateBillablePeriods
job: "UpdateBillablePeriods",
error: "end_date is in the future",
data: {
- end_date: end_date.localtime.strftime("%F %T"),
+ end_date: end_date.in_time_zone.strftime("%F %T"),
now: Time.zone.now.strftime("%F %T")
}
})
diff --git a/app/models/billable_period.rb b/app/models/billable_period.rb
index 79f4b3eaba..d604b5a59c 100644
--- a/app/models/billable_period.rb
+++ b/app/models/billable_period.rb
@@ -1,3 +1,5 @@
+require 'open_food_network/bill_calculator'
+
class BillablePeriod < ActiveRecord::Base
belongs_to :enterprise
belongs_to :owner, class_name: 'Spree::User'
@@ -15,14 +17,9 @@ class BillablePeriod < ActiveRecord::Base
end
def bill
- # Will make this more sophisicated in the future in that it will use global config variables to calculate
return 0 if trial?
- if ['own', 'any'].include? sells
- bill = (turnover * 0.02).round(2)
- bill > 50 ? 50 : bill
- else
- 0
- end
+ return 0 unless ['own', 'any'].include?(sells)
+ OpenFoodNetwork::BillCalculator.new(turnover: turnover).bill
end
def label
@@ -34,8 +31,8 @@ class BillablePeriod < ActiveRecord::Base
end
def adjustment_label
- begins = begins_at.localtime.strftime("%d/%m/%y")
- ends = ends_at.localtime.strftime("%d/%m/%y")
+ begins = begins_at.in_time_zone.strftime("%d/%m/%y")
+ ends = ends_at.in_time_zone.strftime("%d/%m/%y")
"#{label} [#{begins} - #{ends}]"
end
@@ -47,13 +44,14 @@ class BillablePeriod < ActiveRecord::Base
def ensure_correct_adjustment_for(invoice)
if adjustment
# adjustment.originator = enterprise.package
+ adjustment.adjustable = invoice
adjustment.update_attributes( label: adjustment_label, amount: bill )
else
self.adjustment = invoice.adjustments.new( adjustment_attrs, :without_protection => true )
end
- if Spree::Config.account_bill_inc_tax
- adjustment.set_included_tax! Spree::Config.account_bill_tax_rate
+ if Spree::Config.account_invoices_tax_rate > 0
+ adjustment.set_included_tax! Spree::Config.account_invoices_tax_rate
else
adjustment.set_included_tax! 0
end
diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb
index 82054ad792..25e0ced2cc 100644
--- a/app/models/spree/ability_decorator.rb
+++ b/app/models/spree/ability_decorator.rb
@@ -92,6 +92,11 @@ class AbilityDecorator
can [:admin, :known_users], :search
can [:admin, :show], :account
+
+ # For printing own account invoice orders
+ can [:print], Spree::Order do |order|
+ order.user == user
+ end
end
def add_product_management_abilities(user)
diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb
index 2e1c838e95..fc7a8171cc 100644
--- a/app/models/spree/app_configuration_decorator.rb
+++ b/app/models/spree/app_configuration_decorator.rb
@@ -7,8 +7,6 @@ Spree::AppConfiguration.class_eval do
# Tax Preferences
preference :products_require_tax_category, :boolean, default: false
preference :shipping_tax_rate, :decimal, default: 0
- preference :account_bill_inc_tax, :boolean, default: false
- preference :account_bill_tax_rate, :decimal, default: 0
# Accounts & Billing Preferences
preference :accounts_distributor_id, :integer, default: nil
@@ -16,4 +14,10 @@ Spree::AppConfiguration.class_eval do
preference :default_accounts_shipping_method_id, :integer, default: nil
preference :auto_update_invoices, :boolean, default: false
preference :auto_finalize_invoices, :boolean, default: false
+
+ # Business Model Configuration
+ preference :account_invoices_monthly_fixed, :decimal, default: 0
+ preference :account_invoices_monthly_rate, :decimal, default: 0
+ preference :account_invoices_monthly_cap, :decimal, default: 0
+ preference :account_invoices_tax_rate, :decimal, default: 0
end
diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb
index ee4384b460..9db9ae1269 100644
--- a/app/models/spree/line_item_decorator.rb
+++ b/app/models/spree/line_item_decorator.rb
@@ -7,7 +7,7 @@ Spree::LineItem.class_eval do
attr_accessible :max_quantity, :final_weight_volume, :price
attr_accessible :final_weight_volume, :price, :as => :api
- before_save :calculate_final_weight_volume, unless: :final_weight_volume_changed?
+ before_save :calculate_final_weight_volume, if: :quantity_changed?, unless: :final_weight_volume_changed?
after_save :update_units
delegate :unit_description, to: :variant
@@ -88,12 +88,10 @@ Spree::LineItem.class_eval do
private
def calculate_final_weight_volume
- if quantity_changed?
- if final_weight_volume.present?
- self.final_weight_volume = final_weight_volume * quantity / quantity_was
- elsif variant.andand.unit_value
- self.final_weight_volume = ((variant.andand.unit_value) * quantity)
- end
+ if final_weight_volume.present? && quantity_was > 0
+ self.final_weight_volume = final_weight_volume * quantity / quantity_was
+ elsif variant.andand.unit_value.present?
+ self.final_weight_volume = variant.andand.unit_value * quantity
end
end
end
diff --git a/app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface b/app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface
new file mode 100644
index 0000000000..7d5c311a7b
--- /dev/null
+++ b/app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface
@@ -0,0 +1,4 @@
+// insert_bottom "[data-hook='admin_configurations_sidebar_menu']"
+
+%li
+ = link_to 'Business Model', main_app.edit_admin_business_model_configuration_path
diff --git a/app/views/admin/account/show.html.haml b/app/views/admin/account/show.html.haml
index 809d52eb2f..808402ed16 100644
--- a/app/views/admin/account/show.html.haml
+++ b/app/views/admin/account/show.html.haml
@@ -7,34 +7,47 @@
%h4= t(:no_invoices_to_display)
- @invoices.order('year DESC, month DESC').each do |invoice|
- - order = invoice.order
.row.invoice_title
- .eight.columns.alpha
- %h4= "#{t(:abbr_month_names, :scope => :date)[invoice.month]} #{invoice.year}#{invoice.order.completed? ? "" : "*"}"
- .eight.columns.omega.text-right
- %h4.balance= invoice.order.display_total
+ .two.columns.alpha
+ %h4= invoice_description_for(invoice)
+ .two.columns.text-right
+ %h5
+ - if invoice.order.andand.complete?
+ %a{ href: print_admin_order_url(invoice.order), :target => "_blank"}
+ %i.icon-print
+ = t(:print)
+ - else
+
+ .ten.columns
+
+ .two.columns.omega.text-right
+ %h4.balance= invoice_total_for(invoice)
%table.invoice_summary
- %col{ width: '20%' }
- %col{ width: '60%' }
- %col{ width: '20%' }
+ %col{ width: '25%' }
+ %col{ width: '62.5%' }
+ %col{ width: '12.5%' }
%thead
%th Date
%th= t(:description)
%th= t(:charge)
- - invoice.billable_periods.select{ |bp| bp.bill > 0}.each do |billable_period|
- %tr
- %td.text-center= "#{billable_period.begins_at.strftime("%d/%m/%Y")}"
- %td= billable_period.label
- %td.text-right= billable_period.display_bill
- - order.adjustments.where('source_type <> (?)', "BillablePeriod").each do |adjustment|
- %tr
- %td.text-center
- %td= adjustment.label
- %td.text-right= adjustment.display_amount
+ - if order = invoice.order
+ - invoice.billable_periods.select{ |bp| bp.adjustment.andand.amount.andand > 0}.each do |billable_period|
+ %tr
+ %td.text-center= "#{billable_period.begins_at.strftime("%d/%m/%Y")}"
+ %td= billable_period.label
+ -# Using amount from the actual adjustment on the order here so that we avoid recalculating the bill
+ -# at a future date with different settings to those used at the time the invoice was finalized
+ %td.text-right= billable_period.adjustment.display_amount
+ - order.adjustments.where('source_type <> (?)', "BillablePeriod").reject{ |a| a.amount == 0 }.each do |adjustment|
+ %tr
+ %td.text-center
+ %td= adjustment.label
+ %td.text-right= adjustment.display_amount
%tr.total
%td.text-center
%td= t(:total).upcase
- %td.text-right= order.display_total
+ %td.text-right= invoice_total_for(invoice)
+
-# - if @enterprises.empty?
-# %h4 No enterprises to display
@@ -51,8 +64,8 @@
-# %th Bill
-# - enterprise.billable_periods.each do |billable_period|
-# %tr
--# %td= billable_period.begins_at.localtime.strftime("%F %T")
--# %td= billable_period.ends_at.localtime.strftime("%F %T")
+-# %td= billable_period.begins_at.in_time_zone.strftime("%F %T")
+-# %td= billable_period.ends_at.in_time_zone.strftime("%F %T")
-# %td= billable_period.sells
-# %td= billable_period.trial?
-# %td= billable_period.display_turnover
diff --git a/app/views/admin/business_model_configuration/edit.html.haml b/app/views/admin/business_model_configuration/edit.html.haml
new file mode 100644
index 0000000000..09a5949456
--- /dev/null
+++ b/app/views/admin/business_model_configuration/edit.html.haml
@@ -0,0 +1,84 @@
+= render :partial => 'spree/admin/shared/configuration_menu'
+
+- content_for :page_title do
+ %h1.page-title= t(:business_model_configuration)
+ %a.with-tip{ 'data-powertip' => "Configure the rate at which shops will be charged each month for use of the Open Food Network." } What's this?
+
+= render 'spree/shared/error_messages', target: @settings
+
+.row{ ng: { app: 'admin.businessModelConfiguration', controller: "BusinessModelConfigCtrl" } }
+ .five.columns.omega
+ %fieldset.no-border-bottom
+ %legend=t(:bill_calculation_settings)
+ %p
+ Adjust the amount that enterprises will be billed each month for use of the OFN.
+ %br
+ = form_for @settings, as: :settings, url: main_app.admin_business_model_configuration_path, :method => :put do |f|
+ .row
+ .three.columns.alpha
+ = f.label :account_invoices_monthly_fixed, t(:fixed_monthly_charge)
+ %span.with-tip.icon-question-sign{'data-powertip' => "A fixed monthly charge for ALL enterprises who are set up as a shop, regardless of how much produce they sell."}
+ .two.columns.omega
+ .input-symbol.before
+ %span= Spree::Money.currency_symbol
+ = f.number_field :account_invoices_monthly_fixed, min: 0.0, class: "fullwidth", 'watch-value-as' => 'fixed'
+ .row
+ .three.columns.alpha
+ = f.label :account_invoices_monthly_rate, t(:percentage_of_turnover)
+ %span.with-tip.icon-question-sign{'data-powertip' => "When greater than zero, this rate (0.0 - 1.0) will be applied to the total turnover of each shop and added to any fixed charges (to the left) to calculate the monthly bill."}
+ .two.columns.omega
+ = f.number_field :account_invoices_monthly_rate, min: 0.0, max: 1.0, step: 0.01, class: "fullwidth", 'watch-value-as' => 'rate'
+ .row
+ .three.columns.alpha
+ = f.label :account_invoices_monthly_cap, t(:monthly_cap_excl_tax)
+ %span.with-tip.icon-question-sign{'data-powertip' => "When greater than zero, this value will be used as a cap on the amount that shops will be charged each month."}
+ .two.columns.omega
+ .input-symbol.before
+ %span= Spree::Money.currency_symbol
+ = f.number_field :account_invoices_monthly_cap, min: 0.0, class: "fullwidth", 'watch-value-as' => 'cap'
+ .row
+ .three.columns.alpha
+ = f.label :account_invoices_tax_rate, t(:tax_rate)
+ %span.with-tip.icon-question-sign{'data-powertip' => "Tax rate that applies to the the monthly bill that enterprises are charged for using the system."}
+ .two.columns.omega
+ = f.number_field :account_invoices_tax_rate, min: 0.0, max: 1.0, step: 0.01, class: "fullwidth", 'watch-value-as' => 'taxRate'
+
+ .row
+ .five.columns.alpha.omega.form-buttons{"data-hook" => "buttons"}
+ = button t(:update), 'icon-refresh', value: "update"
+
+ .two.columns
+
+
+ .five.columns.alpha
+ %fieldset.no-border-bottom
+ %legend=t(:example_bill_calculator)
+ %p
+ Alter the example turnover to visualise the effect of the settings to the left.
+ %br
+ .row
+ .three.columns.alpha
+ = label_tag :turnover, t(:example_monthly_turnover)
+ %span.with-tip.icon-question-sign{'data-powertip' => "An example monthly turnover for an enterprise which will be used to generate calculate an example monthly bill below."}
+ .two.columns.omega
+ .input-symbol.before
+ %span= Spree::Money.currency_symbol
+ %input.fullwidth{ id: 'turnover', type: "number", ng: { model: 'turnover' } }
+ .row
+ .three.columns.alpha
+ = label_tag :cap_reached, t(:cap_reached?)
+ %span.with-tip.icon-question-sign{'data-powertip' => "Whether the cap (specified to the left) has been reached, given the settings and the turnover provided."}
+ .two.columns.omega
+ %input.fullwidth{ id: 'cap_reached', type: "text", readonly: true, ng: { value: 'capReached()' } }
+ .row
+ .three.columns.alpha
+ = label_tag :included_tax, t(:included_tax)
+ %span.with-tip.icon-question-sign{'data-powertip' => "The total tax included in the example monthly bill, given the settings and the turnover provided."}
+ .two.columns.omega
+ %input.fullwidth{ id: 'included_tax', type: "text", readonly: true, ng: { value: 'includedTax() | currency' } }
+ .row
+ .three.columns.alpha
+ = label_tag :total_incl_tax, t(:total_monthly_bill_incl_tax)
+ %span.with-tip.icon-question-sign{'data-powertip' => "The example total monthly bill with tax included, given the settings and the turnover provided."}
+ .two.columns.omega
+ %input.fullwidth{ id: 'total_incl_tax', type: "text", readonly: true, ng: { value: 'total() | currency' } }
diff --git a/app/views/admin/enterprises/_change_type_form.html.haml b/app/views/admin/enterprises/_change_type_form.html.haml
index 740857e381..6474af1344 100644
--- a/app/views/admin/enterprises/_change_type_form.html.haml
+++ b/app/views/admin/enterprises/_change_type_form.html.haml
@@ -1,4 +1,5 @@
= admin_inject_enterprise
+= admin_inject_monthly_bill_description
= form_for @enterprise, url: main_app.register_admin_enterprise_path(@enterprise),
html: { name: "change_type", id: "change_type", novalidate: true, "ng-app" => "admin.enterprises", "ng-controller"=> 'changeTypeFormCtrl' } do |change_type_form|
@@ -16,9 +17,6 @@
.bottom ALWAYS FREE
%p.description
Add your products to Open Food Network, allowing hubs to stock your products in their stores.
- %br
- %br
- Having a profile, and making connections within your local food system through the Open Food Network will always be free.
.producer_shop.option.one-third.column
%a.full-width.button.selector{ ng: { click: "sells='own'", class: "{selected: sells=='own'}" } }
@@ -26,17 +24,13 @@
%h3 Producer Shop
%p Sell your own produce
.bottom
- \%2 OF SALES
- %br
- CAPPED AT $50 PER MONTH
+ %monthly-pricing-description{ joiner: "newline" }
+
%p.description
Sell your products directly to customers through your very own Open Food Network shopfront.
%br
%br
A Producer Shop is for your produce only, if you want to sell produce grown/produced off site, select 'Producer Hub'.
- %br
- %br
- You will be billed for 2% of your actual transactions, capped at $50 a month (so if you don’t sell anything you don’t pay anything, but you never pay more than $50 a month).
.full_hub.option.one-third.column.omega
%a.full-width.button.selector{ ng: { click: "sells='any'", class: "{selected: sells=='any'}" } }
@@ -44,15 +38,10 @@
%h3 Producer Hub
%p Sell produce from self and others
.bottom
- \%2 OF SALES
- %br
- CAPPED AT $50 PER MONTH
+ %monthly-pricing-description{ joiner: "newline" }
+
%p.description
Your enterprise is the backbone of your local food system. You can sell your own produce as well as produce aggregated from other enterprises through your shopfront on the Open Food Network.
- %br
- %br
- You will be billed for 2% of your actual transactions, capped at $50 a month (so if you don’t sell anything you don’t pay anything, but you never pay more than $50 a month).
-
-# %p.description
-# Test out having your own shopfront with full access to all Shopfront features for 30 days.
@@ -69,9 +58,6 @@
.bottom ALWAYS FREE
%p.description
People can find and contact you on the Open Food Network. Your enterprise will be visible on the map, and will be searchable in listings.
- %br
- %br
- Having a profile, and making connections within your local food system through the Open Food Network will always be free.
.full_hub.option.one-third.column
%a.full-width.button.selector{ ng: { click: "sells='any'", class: "{selected: sells=='any'}" } }
@@ -79,14 +65,10 @@
%h3 Hub Shop
%p Sell produce from others
.bottom
- \%2 OF SALES
- %br
- CAPPED AT $50 PER MONTH
+ %monthly-pricing-description{ joiner: "newline" }
+
%p.description
Your enterprise is the backbone of your local food system. You aggregate produce from other enterprises and can sell it through your shop on the Open Food Network.
- %br
- %br
- You will be billed for 2% of your actual transactions, capped at $50 a month (so if you don’t sell anything you don’t pay anything, but you never pay more than $50 a month).
.row
diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml
index 826d09336c..62bc881728 100644
--- a/app/views/admin/enterprises/index.html.haml
+++ b/app/views/admin/enterprises/index.html.haml
@@ -8,6 +8,7 @@
%li#new_product_link
= button_link_to "New Enterprise", main_app.new_admin_enterprise_path, :icon => 'icon-plus', :id => 'admin_new_enterprise_link'
+= admin_inject_monthly_bill_description
= render 'admin/shared/enterprises_sub_menu'
= render :partial => 'spree/shared/error_messages', :locals => { :target => @enterprise_set }
diff --git a/app/views/spree/admin/orders/invoice.html.haml b/app/views/spree/admin/orders/invoice.html.haml
index 75377b800e..0913ee27db 100644
--- a/app/views/spree/admin/orders/invoice.html.haml
+++ b/app/views/spree/admin/orders/invoice.html.haml
@@ -14,7 +14,7 @@
%td{width: "10%" }
%td{ :align => "right" }
- %h4= @order.order_cycle.name
+ %h4= @order.order_cycle.andand.name
%tr{ valign: "top" }
%td{ :align => "left" }
%strong= "From: #{@order.distributor.name}"
diff --git a/app/views/spree/admin/tax_settings/edit.html.haml b/app/views/spree/admin/tax_settings/edit.html.haml
index 102ec0609e..edd5a3b401 100644
--- a/app/views/spree/admin/tax_settings/edit.html.haml
+++ b/app/views/spree/admin/tax_settings/edit.html.haml
@@ -19,14 +19,5 @@
= number_field_tag "preferences[shipping_tax_rate]", Spree::Config[:shipping_tax_rate].to_f, in: 0.0..1.0, step: 0.01
= label_tag nil, t(:shipping_tax_rate)
- .field.align-center{"data-hook" => "billing_tax"}
- = hidden_field_tag 'preferences[account_bill_inc_tax]', '0'
- = check_box_tag 'preferences[account_bill_inc_tax]', '1', Spree::Config[:account_bill_inc_tax]
- = label_tag nil, t(:account_bill_inc_tax)
-
- .field.align-center{ "data-hook" => "account_bill_tax_rate" }
- = number_field_tag "preferences[account_bill_tax_rate]", Spree::Config[:account_bill_tax_rate].to_f, in: 0.0..1.0, step: 0.01
- = label_tag nil, t(:account_bill_tax_rate)
-
.form-buttons{"data-hook" => "buttons"}
= button t(:update), 'icon-refresh'
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 373440ebfe..c9df13af92 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -30,6 +30,15 @@ en:
confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?"
confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?"
must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent."
+ invoice: "Invoice"
+ percentage_of_sales: "%{percentage} of sales"
+ percentage_of_turnover: "Percentage of turnover"
+ monthly_cap_excl_tax: "monthly cap (excl. GST)"
+ capped_at_cap: "capped at %{cap}"
+ per_month: "per month"
+ free: "free"
+ plus_tax: "plus GST"
+ total_monthly_bill_incl_tax: "Total Monthly Bill (Incl. Tax)"
sort_order_cycles_on_shopfront_by: "Sort Order Cycles On Shopfront By"
@@ -55,7 +64,6 @@ en:
footer_links_md: "Links"
footer_about_url: "About URL"
footer_tos_url: "Terms of Service URL"
- invoice: "Invoice"
name: Name
first_name: First Name
diff --git a/config/routes.rb b/config/routes.rb
index eb8c228b91..c8771560e9 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -116,6 +116,8 @@ Openfoodnetwork::Application.routes.draw do
end
end
+ resource :business_model_configuration, only: [:edit, :update], controller: 'business_model_configuration'
+
resource :account, only: [:show], controller: 'account'
end
diff --git a/lib/open_food_network/bill_calculator.rb b/lib/open_food_network/bill_calculator.rb
new file mode 100644
index 0000000000..84457d6530
--- /dev/null
+++ b/lib/open_food_network/bill_calculator.rb
@@ -0,0 +1,19 @@
+module OpenFoodNetwork
+ class BillCalculator
+ attr_accessor :turnover, :fixed, :rate, :cap, :tax_rate
+
+ def initialize(opts={})
+ @turnover = opts[:turnover] || 0
+ @fixed = opts[:fixed] || Spree::Config[:account_invoices_monthly_fixed]
+ @rate = opts[:rate] || Spree::Config[:account_invoices_monthly_rate]
+ @cap = opts[:cap] || Spree::Config[:account_invoices_monthly_cap]
+ @tax_rate = opts[:tax_rate] || Spree::Config[:account_invoices_tax_rate]
+ end
+
+ def bill
+ bill = fixed + (turnover * rate)
+ bill = cap > 0 ? [bill, cap].min : bill
+ bill * (1 + tax_rate)
+ end
+ end
+end
diff --git a/lib/open_food_network/business_model_configuration_validator.rb b/lib/open_food_network/business_model_configuration_validator.rb
new file mode 100644
index 0000000000..d83d94ffc1
--- /dev/null
+++ b/lib/open_food_network/business_model_configuration_validator.rb
@@ -0,0 +1,20 @@
+# This class is a lightweight model used to validate preferences for business model configuration
+# when they are submitted to the BusinessModelConfigurationController
+
+module OpenFoodNetwork
+ class BusinessModelConfigurationValidator
+ include ActiveModel::Validations
+
+ attr_accessor :account_invoices_monthly_fixed, :account_invoices_monthly_rate, :account_invoices_monthly_cap, :account_invoices_tax_rate
+
+ validates :account_invoices_monthly_fixed, presence: true, numericality: { greater_than_or_equal_to: 0 }
+ validates :account_invoices_monthly_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }
+ validates :account_invoices_monthly_cap, presence: true, numericality: { greater_than_or_equal_to: 0 }
+ validates :account_invoices_tax_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }
+
+ def initialize(attr, button=nil)
+ attr.each { |k,v| instance_variable_set("@#{k}", v) }
+ @button = button
+ end
+ end
+end
diff --git a/lib/spree/money_decorator.rb b/lib/spree/money_decorator.rb
index fed92b8210..3479bbd9a2 100644
--- a/lib/spree/money_decorator.rb
+++ b/lib/spree/money_decorator.rb
@@ -4,4 +4,9 @@ Spree::Money.class_eval do
def self.currency_symbol
Money.new(0, Spree::Config[:currency]).symbol
end
+
+ def rounded
+ @options[:no_cents] = true if @money.amount % 1 == 0
+ to_s
+ end
end
diff --git a/spec/controllers/admin/business_model_configuration_controller_spec.rb b/spec/controllers/admin/business_model_configuration_controller_spec.rb
new file mode 100644
index 0000000000..b0ae86d4ac
--- /dev/null
+++ b/spec/controllers/admin/business_model_configuration_controller_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe Admin::BusinessModelConfigurationController, type: :controller do
+ let(:user) { create(:user) }
+ let(:admin) { create(:admin_user) }
+
+ before do
+ Spree::Config.set({
+ account_invoices_monthly_fixed: 5,
+ account_invoices_monthly_rate: 0.02,
+ account_invoices_monthly_cap: 50,
+ account_invoices_tax_rate: 0.1
+ })
+ end
+
+ describe "edit" do
+ context "as an enterprise user" do
+ before { allow(controller).to receive(:spree_current_user) { user } }
+
+ it "does not allow access" do
+ spree_get :edit
+ expect(response).to redirect_to spree.unauthorized_path
+ end
+ end
+
+ context "as super admin" do
+ before { allow(controller).to receive(:spree_current_user) { admin } }
+
+ it "allows access" do
+ spree_get :edit
+ expect(response).to_not redirect_to spree.unauthorized_path
+ end
+ end
+ end
+
+ describe "update" do
+ context "as an enterprise user" do
+ before { allow(controller).to receive(:spree_current_user) { user } }
+
+ it "does not allow access" do
+ spree_get :update
+ expect(response).to redirect_to spree.unauthorized_path
+ end
+ end
+
+ context "as super admin" do
+ before {allow(controller).to receive(:spree_current_user) { admin } }
+ let(:params) { { settings: { } } }
+
+ context "when settings are invalid" do
+ before do
+ params[:settings][:account_invoices_monthly_fixed] = ''
+ params[:settings][:account_invoices_monthly_rate] = '2'
+ params[:settings][:account_invoices_monthly_cap] = '-1'
+ params[:settings][:account_invoices_tax_rate] = '4'
+ spree_get :update, params
+ end
+
+ it "does not allow them to be set" do
+ expect(response).to render_template :edit
+ expect(assigns(:settings).errors.count).to be 5
+ expect(Spree::Config.account_invoices_monthly_fixed).to eq 5
+ expect(Spree::Config.account_invoices_monthly_rate).to eq 0.02
+ expect(Spree::Config.account_invoices_monthly_cap).to eq 50
+ expect(Spree::Config.account_invoices_tax_rate).to eq 0.1
+ end
+ end
+
+ context "when required settings are valid" do
+ before do
+ params[:settings][:account_invoices_monthly_fixed] = '10'
+ params[:settings][:account_invoices_monthly_rate] = '0.05'
+ params[:settings][:account_invoices_monthly_cap] = '30'
+ params[:settings][:account_invoices_tax_rate] = '0.15'
+ end
+
+ it "sets global config to the specified values" do
+ spree_get :update, params
+ expect(assigns(:settings).errors.count).to be 0
+ expect(Spree::Config.account_invoices_monthly_fixed).to eq 10
+ expect(Spree::Config.account_invoices_monthly_rate).to eq 0.05
+ expect(Spree::Config.account_invoices_monthly_cap).to eq 30
+ expect(Spree::Config.account_invoices_tax_rate).to eq 0.15
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/admin/business_model_configuration_spec.rb b/spec/features/admin/business_model_configuration_spec.rb
new file mode 100644
index 0000000000..05d5367437
--- /dev/null
+++ b/spec/features/admin/business_model_configuration_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+feature 'Business Model Configuration' do
+ include AuthenticationWorkflow
+ include WebHelper
+
+ describe "updating" do
+ let!(:admin) { create(:admin_user) }
+
+ before do
+ Spree::Config.set({
+ account_invoices_monthly_fixed: 5,
+ account_invoices_monthly_rate: 0.02,
+ account_invoices_monthly_cap: 50,
+ account_invoices_tax_rate: 0.1
+ })
+ end
+
+ before do
+ quick_login_as_admin
+ end
+
+ context "as an admin user", js: true do
+ it "loads the page" do
+ visit spree.admin_path
+ click_link "Configuration"
+ click_link "Business Model"
+
+ expect(page).to have_field "settings_account_invoices_monthly_fixed", with: 5.0
+ expect(page).to have_field "settings_account_invoices_monthly_rate", with: 0.02
+ expect(page).to have_field "settings_account_invoices_monthly_cap", with: 50.0
+ expect(page).to have_field "settings_account_invoices_tax_rate", with: 0.1
+ end
+
+ it "attributes can be changed", js: true do
+ visit edit_admin_business_model_configuration_path
+
+ fill_in "settings_account_invoices_monthly_fixed", with: 10
+ fill_in "settings_account_invoices_monthly_rate", with: 0.05
+ fill_in "settings_account_invoices_monthly_cap", with: 30
+ fill_in "settings_account_invoices_tax_rate", with: 0.15
+
+ click_button "Update"
+
+ expect(Spree::Config.account_invoices_monthly_fixed).to eq 10
+ expect(Spree::Config.account_invoices_monthly_rate).to eq 0.05
+ expect(Spree::Config.account_invoices_monthly_cap).to eq 30
+ expect(Spree::Config.account_invoices_tax_rate).to eq 0.15
+ end
+ end
+ end
+end
diff --git a/spec/features/admin/tax_settings_spec.rb b/spec/features/admin/tax_settings_spec.rb
index 9894ee8a72..4b2dd4486c 100644
--- a/spec/features/admin/tax_settings_spec.rb
+++ b/spec/features/admin/tax_settings_spec.rb
@@ -11,9 +11,7 @@ feature 'Account and Billing Settings' do
Spree::Config.set({
products_require_tax_category: false,
shipment_inc_vat: false,
- shipping_tax_rate: 0,
- account_bill_inc_tax: false,
- account_bill_tax_rate: 0
+ shipping_tax_rate: 0
})
end
@@ -30,8 +28,6 @@ feature 'Account and Billing Settings' do
expect(page).to have_unchecked_field 'preferences_products_require_tax_category'
expect(page).to have_unchecked_field 'preferences_shipment_inc_vat'
expect(page).to have_field 'preferences_shipping_tax_rate'
- expect(page).to have_unchecked_field 'preferences_account_bill_inc_tax'
- expect(page).to have_field 'preferences_account_bill_tax_rate'
end
it "attributes can be changed" do
@@ -40,16 +36,12 @@ feature 'Account and Billing Settings' do
check 'preferences_products_require_tax_category'
check 'preferences_shipment_inc_vat'
fill_in 'preferences_shipping_tax_rate', with: '0.12'
- check 'preferences_account_bill_inc_tax'
- fill_in 'preferences_account_bill_tax_rate', with: '0.05'
click_button "Update"
expect(Spree::Config.products_require_tax_category).to be true
expect(Spree::Config.shipment_inc_vat).to be true
expect(Spree::Config.shipping_tax_rate).to eq 0.12
- expect(Spree::Config.account_bill_inc_tax).to be true
- expect(Spree::Config.account_bill_tax_rate).to eq 0.05
end
end
end
diff --git a/spec/helpers/admin/business_model_configuration_helper_spec.rb b/spec/helpers/admin/business_model_configuration_helper_spec.rb
new file mode 100644
index 0000000000..a10771cba7
--- /dev/null
+++ b/spec/helpers/admin/business_model_configuration_helper_spec.rb
@@ -0,0 +1,139 @@
+require 'spec_helper'
+
+describe Admin::BusinessModelConfigurationHelper do
+ describe "describing monthly bills for enterprises" do
+ context "when tax is applied to the service change" do
+ before { Spree::Config.set(:account_invoices_tax_rate, 0.1) }
+ context "when a fixed cost is included" do
+ before { Spree::Config.set(:account_invoices_monthly_fixed, 10) }
+
+ context "when a percentage of turnover is included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(helper.monthly_bill_description).to eq "$10 + 5.0% OF SALES{joiner}CAPPED AT $20 PER MONTH, PLUS GST" }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(helper.monthly_bill_description).to eq "$10 + 5.0% OF SALES PER MONTH, PLUS GST" }
+ end
+ end
+
+ context "when a percentage of turnover is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(helper.monthly_bill_description).to eq "$10 PER MONTH, PLUS GST" }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(helper.monthly_bill_description).to eq "$10 PER MONTH, PLUS GST" }
+ end
+ end
+ end
+
+ context "when a fixed cost is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_fixed, 0) }
+
+ context "when a percentage of turnover is included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES{joiner}CAPPED AT $20 PER MONTH, PLUS GST" }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES PER MONTH, PLUS GST" }
+ end
+ end
+
+ context "when a percentage of turnover is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(helper.monthly_bill_description).to eq "FREE" }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(helper.monthly_bill_description).to eq "FREE" }
+ end
+ end
+ end
+ end
+
+ context "when tax is applied to the service change" do
+ before { Spree::Config.set(:account_invoices_tax_rate, 0.0) }
+ context "when a fixed cost is included" do
+ before { Spree::Config.set(:account_invoices_monthly_fixed, 10) }
+
+ context "when a percentage of turnover is included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(helper.monthly_bill_description).to eq "$10 + 5.0% OF SALES{joiner}CAPPED AT $20 PER MONTH" }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(helper.monthly_bill_description).to eq "$10 + 5.0% OF SALES PER MONTH" }
+ end
+ end
+
+ context "when a percentage of turnover is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(helper.monthly_bill_description).to eq "$10 PER MONTH" }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(helper.monthly_bill_description).to eq "$10 PER MONTH" }
+ end
+ end
+ end
+
+ context "when a fixed cost is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_fixed, 0) }
+
+ context "when a percentage of turnover is included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES{joiner}CAPPED AT $20 PER MONTH" }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES PER MONTH" }
+ end
+ end
+
+ context "when a percentage of turnover is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(helper.monthly_bill_description).to eq "FREE" }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(helper.monthly_bill_description).to eq "FREE" }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/jobs/finalize_account_invoices_spec.rb b/spec/jobs/finalize_account_invoices_spec.rb
index e169df93e5..d4b250d42f 100644
--- a/spec/jobs/finalize_account_invoices_spec.rb
+++ b/spec/jobs/finalize_account_invoices_spec.rb
@@ -184,6 +184,11 @@ describe FinalizeAccountInvoices do
Spree::Config.set({ accounts_distributor_id: accounts_distributor.id })
Spree::Config.set({ default_accounts_payment_method_id: pm.id })
Spree::Config.set({ default_accounts_shipping_method_id: sm.id })
+
+ # Make sure that bills are > 0
+ Spree::Config.set(:account_invoices_monthly_fixed, 5)
+ Spree::Config.set(:account_invoices_monthly_rate, 0.02)
+ Spree::Config.set(:account_invoices_monthly_cap, 50)
end
context "finalizing an invoice" do
@@ -200,9 +205,9 @@ describe FinalizeAccountInvoices do
invoice.reload
expect(invoice.completed_at).to_not be_nil
- expect(invoice.total).to eq billable_period1.bill
+ expect(invoice.total).to eq billable_period1.bill.round(2)
expect(invoice.payments.count).to eq 1
- expect(invoice.payments.first.amount).to eq billable_period1.bill
+ expect(invoice.payments.first.amount).to eq billable_period1.bill.round(2)
expect(invoice.state).to eq 'complete'
end
end
diff --git a/spec/jobs/update_account_invoices_spec.rb b/spec/jobs/update_account_invoices_spec.rb
index a97811fc86..87d601b60a 100644
--- a/spec/jobs/update_account_invoices_spec.rb
+++ b/spec/jobs/update_account_invoices_spec.rb
@@ -7,17 +7,28 @@ end
describe UpdateAccountInvoices do
let(:year) { Time.zone.now.year }
+ before do
+ # Make sure that bills are > 0
+ Spree::Config.set(:account_invoices_monthly_fixed, 5)
+ Spree::Config.set(:account_invoices_monthly_rate, 0.02)
+ Spree::Config.set(:account_invoices_monthly_cap, 50)
+ end
+
describe "units specs" do
let!(:start_of_july) { Time.zone.local(year, 7) }
let!(:updater) { UpdateAccountInvoices.new }
let!(:user) { create(:user) }
- let!(:old_billable_period) { create(:billable_period, owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july) }
- let!(:billable_period1) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) }
- let!(:billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) }
- let(:june_account_invoice) { old_billable_period.account_invoice }
- let(:july_account_invoice) { billable_period1.account_invoice }
+ let!(:june_billable_period1) { create(:billable_period, owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july - 20.days) }
+ let!(:june_billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july - 20.days, ends_at: start_of_july - 10.days, turnover: 45, sells: "none" ) }
+ let!(:june_billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july - 10.days, ends_at: start_of_july - 1.days, turnover: 0, sells: "any" ) }
+ let!(:july_billable_period1) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) }
+ let!(:july_billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) }
+ let!(:july_billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july + 20.days, ends_at: start_of_july + 25.days, turnover: 45, sells: 'none') }
+ let!(:july_billable_period4) { create(:billable_period, owner: user, begins_at: start_of_july + 25.days, ends_at: start_of_july + 28.days, turnover: 0, sells: 'any') }
+ let(:june_account_invoice) { june_billable_period1.account_invoice }
+ let(:july_account_invoice) { july_billable_period1.account_invoice }
describe "perform" do
let(:accounts_distributor) { double(:accounts_distributor) }
@@ -145,21 +156,27 @@ describe UpdateAccountInvoices do
context "where the order is not complete" do
before do
allow(invoice_order).to receive(:complete?) { false }
+ june_billable_period1.enterprise.update_attributes(contact: "Firstname Lastname Something Else", phone: '12345')
updater.update(june_account_invoice)
end
- it "creates adjustments for each billing item" do
+ it "creates adjustments for each billing item where bill is not 0" do
adjustments = invoice_order.adjustments
- expect(adjustments.map(&:source_id)).to eq [old_billable_period.id]
- expect(adjustments.map(&:amount)).to eq [old_billable_period.bill]
- expect(adjustments.map(&:label)).to eq [old_billable_period.adjustment_label]
+ expect(adjustments.map(&:source_id)).to eq [june_billable_period1.id, june_billable_period3.id]
+ expect(adjustments.map(&:amount)).to eq [june_billable_period1.bill.round(2), june_billable_period3.bill.round(2)]
+ expect(adjustments.map(&:label)).to eq [june_billable_period1.adjustment_label, june_billable_period3.adjustment_label]
end
it "assigns a addresses to the order" do
expect(invoice_order.billing_address).to be_a Spree::Address
expect(invoice_order.shipping_address).to be_a Spree::Address
- expect(invoice_order.billing_address).to eq old_billable_period.enterprise.address
- expect(invoice_order.shipping_address).to eq old_billable_period.enterprise.address
+ expect(invoice_order.shipping_address).to eq invoice_order.billing_address
+ [:address1, :address2, :city, :zipcode, :state_id, :country_id].each do |attr|
+ expect(invoice_order.billing_address[attr]).to eq june_billable_period1.enterprise.address[attr]
+ end
+ expect(invoice_order.billing_address.firstname).to eq "Firstname"
+ expect(invoice_order.billing_address.lastname).to eq "Lastname Something Else"
+ expect(invoice_order.billing_address.phone).to eq "12345"
end
it "saves the order" do
@@ -180,11 +197,11 @@ describe UpdateAccountInvoices do
updater.update(july_account_invoice)
end
- it "creates adjustments for each billing item" do
+ it "creates adjustments for each billing item where bill is not 0" do
adjustments = july_account_invoice.order.adjustments
- expect(adjustments.map(&:source_id)).to eq [billable_period1.id, billable_period2.id]
- expect(adjustments.map(&:amount)).to eq [billable_period1.bill, billable_period2.bill]
- expect(adjustments.map(&:label)).to eq [billable_period1.adjustment_label, billable_period2.adjustment_label]
+ expect(adjustments.map(&:source_id)).to eq [july_billable_period1.id, july_billable_period2.id,july_billable_period4.id]
+ expect(adjustments.map(&:amount)).to eq [july_billable_period1.bill.round(2), july_billable_period2.bill.round(2), july_billable_period4.bill.round(2)]
+ expect(adjustments.map(&:label)).to eq [july_billable_period1.adjustment_label, july_billable_period2.adjustment_label, july_billable_period4.adjustment_label]
end
it "saves the order" do
@@ -328,14 +345,15 @@ describe UpdateAccountInvoices do
let!(:accounts_distributor) { create(:distributor_enterprise) }
let!(:user) { create(:user) }
- let!(:billable_period1) { create(:billable_period, sells: 'any', owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july) }
- let!(:billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 10.days) }
- let!(:billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) }
- let!(:july_account_invoice) { billable_period2.account_invoice }
+ let!(:july_billable_period1) { create(:billable_period, sells: 'any', owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july) }
+ let!(:july_billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 10.days) }
+ let!(:july_billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) }
+ let!(:july_account_invoice) { july_billable_period2.account_invoice }
let!(:august_account_invoice) { create(:account_invoice, user: user, year: july_account_invoice.year, month: 8)}
before do
Spree::Config.set({ accounts_distributor_id: accounts_distributor.id })
+ july_billable_period2.enterprise.update_attributes(contact: 'Anna Karenina', phone: '3433523')
end
context "when no invoice_order currently exists" do
@@ -348,14 +366,19 @@ describe UpdateAccountInvoices do
expect(user.orders.first).to eq invoice_order
expect(invoice_order.completed_at).to be_nil
billable_adjustments = invoice_order.adjustments.where('source_type = (?)', 'BillablePeriod')
- expect(billable_adjustments.map(&:amount)).to eq [billable_period2.bill, billable_period3.bill]
- expect(invoice_order.total).to eq billable_period2.bill + billable_period3.bill
+ expect(billable_adjustments.map(&:amount)).to eq [july_billable_period2.bill.round(2), july_billable_period3.bill.round(2)]
+ expect(invoice_order.total).to eq july_billable_period2.bill.round(2) + july_billable_period3.bill.round(2)
expect(invoice_order.payments.count).to eq 0
expect(invoice_order.state).to eq 'cart'
expect(invoice_order.bill_address).to be_a Spree::Address
expect(invoice_order.ship_address).to be_a Spree::Address
- expect(invoice_order.bill_address).to eq billable_period2.enterprise.address
- expect(invoice_order.ship_address).to eq billable_period2.enterprise.address
+ expect(invoice_order.shipping_address).to eq invoice_order.billing_address
+ [:address1, :address2, :city, :zipcode, :state_id, :country_id].each do |attr|
+ expect(invoice_order.billing_address[attr]).to eq july_billable_period2.enterprise.address[attr]
+ end
+ expect(invoice_order.billing_address.firstname).to eq "Anna"
+ expect(invoice_order.billing_address.lastname).to eq "Karenina"
+ expect(invoice_order.billing_address.phone).to eq "3433523"
end
end
@@ -387,14 +410,19 @@ describe UpdateAccountInvoices do
expect(invoice_order.completed_at).to be_nil
billable_adjustments = invoice_order.adjustments.where('source_type = (?)', 'BillablePeriod')
expect(billable_adjustments).to_not include billable_adjustment
- expect(billable_adjustments.map(&:amount)).to eq [billable_period2.bill, billable_period3.bill]
- expect(invoice_order.total).to eq billable_period2.bill + billable_period3.bill
+ expect(billable_adjustments.map(&:amount)).to eq [july_billable_period2.bill.round(2), july_billable_period3.bill.round(2)]
+ expect(invoice_order.total).to eq july_billable_period2.bill.round(2) + july_billable_period3.bill.round(2)
expect(invoice_order.payments.count).to eq 0
expect(invoice_order.state).to eq 'cart'
expect(invoice_order.bill_address).to be_a Spree::Address
expect(invoice_order.ship_address).to be_a Spree::Address
- expect(invoice_order.bill_address).to eq billable_period2.enterprise.address
- expect(invoice_order.ship_address).to eq billable_period2.enterprise.address
+ expect(invoice_order.shipping_address).to eq invoice_order.billing_address
+ [:address1, :address2, :city, :zipcode, :state_id, :country_id].each do |attr|
+ expect(invoice_order.billing_address[attr]).to eq july_billable_period2.enterprise.address[attr]
+ end
+ expect(invoice_order.billing_address.firstname).to eq "Anna"
+ expect(invoice_order.billing_address.lastname).to eq "Karenina"
+ expect(invoice_order.billing_address.phone).to eq "3433523"
end
end
diff --git a/spec/jobs/update_billable_periods_spec.rb b/spec/jobs/update_billable_periods_spec.rb
index bfdbfa8434..d868550e7e 100644
--- a/spec/jobs/update_billable_periods_spec.rb
+++ b/spec/jobs/update_billable_periods_spec.rb
@@ -524,6 +524,8 @@ describe UpdateBillablePeriods do
let!(:bp6) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july - 10.days, ends_at: start_of_july - 5.days ) }
# Updated before start but ends at start_date (ie. not after start_date, so should be ignored) EDGE CASE
let!(:bp7) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july - 5.days, ends_at: start_of_july ) }
+ # Updated before start, but order is already complete, so should not be deleted
+ let!(:bp8) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july, ends_at: start_of_july + 10.days, account_invoice: create(:account_invoice, order: create(:order, state: 'complete', completed_at: 5.minutes.ago))) }
before do
allow(Bugsnag).to receive(:notify)
@@ -540,6 +542,7 @@ describe UpdateBillablePeriods do
expect(bp5.reload.deleted_at).to be_nil
expect(bp6.reload.deleted_at).to be_nil
expect(bp7.reload.deleted_at).to be_nil
+ expect(bp8.reload.deleted_at).to be_nil
end
it "notifies bugsnag" do
diff --git a/spec/models/billable_period_spec.rb b/spec/models/billable_period_spec.rb
index 1ad974d804..3037ebc6bc 100644
--- a/spec/models/billable_period_spec.rb
+++ b/spec/models/billable_period_spec.rb
@@ -1,27 +1,214 @@
require 'spec_helper'
-describe Customer, type: :model do
+describe BillablePeriod, type: :model do
+
+ require 'spec_helper'
+
describe 'ensure_correct_adjustment' do
let!(:start_of_july) { Time.zone.now.beginning_of_year + 6.months }
let!(:user) { create(:user) }
let!(:invoice) { create(:order, user: user) }
- let!(:billable_period) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) }
+ let!(:subject) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) }
before do
- allow(billable_period).to receive(:bill) { 99 }
- allow(billable_period).to receive(:adjustment_label) { "Label for adjustment" }
- Spree::Config.set({ account_bill_inc_tax: true })
- Spree::Config.set({ account_bill_tax_rate: 0.1 })
+ allow(subject).to receive(:bill) { 99 }
+ allow(subject).to receive(:adjustment_label) { "Label for adjustment" }
+ Spree::Config.set({ account_invoices_tax_rate: 0.1 })
end
context "when no adjustment currently exists" do
it "creates an adjustment on the given order" do
expect(invoice.total_tax).to eq 0.0
- expect(billable_period.adjustment).to be nil
- billable_period.ensure_correct_adjustment_for(invoice)
- expect(billable_period.adjustment).to be_a Spree::Adjustment
+ expect(subject.adjustment).to be nil
+ subject.ensure_correct_adjustment_for(invoice)
+ expect(subject.adjustment).to be_a Spree::Adjustment
expect(invoice.total_tax).to eq 9.0
end
end
end
+
+
+ describe "calculating monthly bills for enterprises" do
+ let!(:subject) { create(:billable_period, turnover: 100) }
+
+ context "when no tax is charged" do
+ before { Spree::Config.set(:account_invoices_tax_rate, 0) }
+
+ context "when a fixed cost is included" do
+ before { Spree::Config.set(:account_invoices_monthly_fixed, 10) }
+
+ context "when a percentage of turnover is included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) }
+
+ context "when the bill is capped" do
+ context "at a level higher than the fixed charge plus the product of the rate and turnover" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 65) }
+ it { expect(subject.bill).to eq 60 }
+ end
+
+ context "at a level lower than the fixed charge plus the product of the rate and turnover" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 55) }
+ it { expect(subject.bill).to eq 55 }
+ end
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(subject.bill).to eq 60 }
+ end
+ end
+
+ context "when a percentage of turnover is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0) }
+
+ context "when the bill is capped" do
+ context "at a level higher than the fixed charge" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 15) }
+ it { expect(subject.bill).to eq 10 }
+ end
+
+ context "at a level lower than the fixed charge" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 5) }
+ it { expect(subject.bill).to eq 5 }
+ end
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(subject.bill).to eq 10 }
+ end
+ end
+ end
+
+ context "when a fixed cost is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_fixed, 0) }
+
+ context "when a percentage of turnover is included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) }
+
+ context "when the bill is capped" do
+ context "at a level higher than the product of the rate and turnover" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 55) }
+ it { expect(subject.bill).to eq 50 }
+ end
+
+ context "at a level lower than the product of the rate and turnover" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 45) }
+ it { expect(subject.bill).to eq 45 }
+ end
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(subject.bill).to eq 50 }
+ end
+ end
+
+ context "when a percentage of turnover is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(subject.bill).to eq 0 }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(subject.bill).to eq 0 }
+ end
+ end
+ end
+ end
+
+ context "when tax is charged" do
+ before { Spree::Config.set(:account_invoices_tax_rate, 0.1) }
+
+ context "when a fixed cost is included" do
+ before { Spree::Config.set(:account_invoices_monthly_fixed, 10) }
+
+ context "when a percentage of turnover is included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) }
+
+ context "when the bill is capped" do
+ context "at a level higher than the fixed charge plus the product of the rate and turnover" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 61) }
+ it { expect(subject.bill).to eq 66 }
+ end
+
+ context "at a level lower than the fixed charge plus the product of the rate and turnover" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 59) }
+ it {
+ expect(subject.bill.to_f).to eq 64.9
+ }
+ end
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(subject.bill).to eq 66 }
+ end
+ end
+
+ context "when a percentage of turnover is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0) }
+
+ context "when the bill is capped" do
+ context "at a level higher than the fixed charge" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 11) }
+ it { expect(subject.bill).to eq 11 }
+ end
+
+ context "at a level lower than the fixed charge" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 9) }
+ it { expect(subject.bill.to_f).to eq 9.9 }
+ end
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(subject.bill).to eq 11 }
+ end
+ end
+ end
+
+ context "when a fixed cost is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_fixed, 0) }
+
+ context "when a percentage of turnover is included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) }
+
+ context "when the bill is capped" do
+ context "at a level higher than the product of the rate and turnover" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 51) }
+ it { expect(subject.bill).to eq 55 }
+ end
+
+ context "at a level lower than the product of the rate and turnover" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 49) }
+ it { expect(subject.bill.to_f).to eq 53.9 }
+ end
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(subject.bill).to eq 55 }
+ end
+ end
+
+ context "when a percentage of turnover is not included" do
+ before { Spree::Config.set(:account_invoices_monthly_rate, 0) }
+
+ context "when the bill is capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 20) }
+ it { expect(subject.bill).to eq 0 }
+ end
+
+ context "when the bill is not capped" do
+ before { Spree::Config.set(:account_invoices_monthly_cap, 0) }
+ it { expect(subject.bill).to eq 0 }
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb
index ecbd3ae610..6bb18fafa3 100644
--- a/spec/models/spree/line_item_spec.rb
+++ b/spec/models/spree/line_item_spec.rb
@@ -165,27 +165,57 @@ module Spree
end
context "and quantity is changed" do
- context "and a final_weight_volume has been set" do
- before do
- expect(expect(li.final_weight_volume).to eq 3000)
- attrs.merge!( quantity: 4 )
- li.update_attributes(attrs)
+ context "from > 0" do
+ context "and a final_weight_volume has been set" do
+ before do
+ expect(li.final_weight_volume).to eq 3000
+ attrs.merge!( quantity: 4 )
+ li.update_attributes(attrs)
+ end
+
+ it "scales the final_weight_volume based on the change in quantity" do
+ expect(li.final_weight_volume).to eq 4000
+ end
end
- it "calculates a final_weight_volume from the variants unit_value" do
- expect(li.final_weight_volume).to eq 4000
+ context "and a final_weight_volume has not been set" do
+ before do
+ li.update_attributes(final_weight_volume: nil)
+ attrs.merge!( quantity: 1 )
+ li.update_attributes(attrs)
+ end
+
+ it "calculates a final_weight_volume from the variants unit_value" do
+ expect(li.final_weight_volume).to eq 1000
+ end
end
end
- context "and a final_weight_volume has not been set" do
- before do
- li.update_attributes(final_weight_volume: nil)
- attrs.merge!( quantity: 1 )
- li.update_attributes(attrs)
+ context "from 0" do
+ before { li.update_attributes(quantity: 0) }
+
+ context "and a final_weight_volume has been set" do
+ before do
+ expect(li.final_weight_volume).to eq 0
+ attrs.merge!( quantity: 4 )
+ li.update_attributes(attrs)
+ end
+
+ it "recalculates a final_weight_volume from the variants unit_value" do
+ expect(li.final_weight_volume).to eq 4000
+ end
end
- it "calculates a final_weight_volume from the variants unit_value" do
- expect(li.final_weight_volume).to eq 1000
+ context "and a final_weight_volume has not been set" do
+ before do
+ li.update_attributes(final_weight_volume: nil)
+ attrs.merge!( quantity: 1 )
+ li.update_attributes(attrs)
+ end
+
+ it "calculates a final_weight_volume from the variants unit_value" do
+ expect(li.final_weight_volume).to eq 1000
+ end
end
end
end
@@ -196,7 +226,7 @@ module Spree
describe "generating the full name" do
let(:li) { LineItem.new }
- context "when display_name is blank" do
+ context "when display_name is blank" do
before do
li.stub(:unit_to_display) { 'unit_to_display' }
li.stub(:display_name) { '' }
@@ -223,7 +253,7 @@ module Spree
li.stub(:unit_to_display) { '10kg' }
li.stub(:display_name) { '10kg Box' }
end
-
+
it "returns display_name" do
li.full_name.should == '10kg Box'
end
@@ -234,7 +264,7 @@ module Spree
li.stub(:unit_to_display) { '1 Loaf' }
li.stub(:display_name) { 'Spelt Sourdough' }
end
-
+
it "returns unit_to_display" do
li.full_name.should == 'Spelt Sourdough (1 Loaf)'
end