Skip to content
This repository has been archived by the owner on Mar 9, 2021. It is now read-only.

Commit

Permalink
Merge pull request #155 from alphagov/feature-all-child-benefit-changes
Browse files Browse the repository at this point in the history
New feature: Part tax year children
  • Loading branch information
leenagupte authored Aug 19, 2016
2 parents 758cabc + fefcdfa commit 39b3cde
Show file tree
Hide file tree
Showing 13 changed files with 683 additions and 256 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ Calculators
This is an application to contain custom-built calculators. These will initially replace some smart-answers that have
outgrown the framework.

## Child benefit tax calculator
Currently the only calculator in this application is the Child benefit tax calculator.

This calculator reports how much child benefit tax you are entitled during a tax year.

There is a cut-off date of 7 January 2013. This is the date [High Income Child Benefit Tax Charge](https://www.gov.uk/child-benefit-tax-charge/overview) came in effect.
This means that if the 2012 tax year is selected the calculator will only calculate the child benefit you are entitled to from 7 Jan 2013 to 5 Apr 2013, not for the entire tax year.


## Running the app

```
Expand Down
56 changes: 42 additions & 14 deletions app/assets/javascripts/child-benefit-tax-calculator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(function () {
(function() {
"use strict";

var root = this,
Expand All @@ -7,17 +7,45 @@

var calculator = {
childrenCountInput: $("#children_count"),
childrenContainer: $("fieldset#children"),

setEventHandlers: function () {
calculator.childrenCountInput.on('change', calculator.updateChildrenFields);
partYearChildrenCountInput: function(){
return $("div#children #part_year_children_count")
},
childrenContainerTemplate: $("div#children-template"),
taxClaimContainer: $("fieldset#is-part-year-claim"),
taxClaimDurationInputs: $("input[id^='is_part_year_claim_']"),
childrenContainer: function() {
return $("div#children");
},
setEventHandlers: function() {
calculator.taxClaimDurationInputs.on('change', calculator.triggerChildrenFieldsEvent);
calculator.setUpForm();
},
setUpForm: function(){
var choosenTaxClaim = calculator.taxClaimDurationInputs.filter(":checked").val();
calculator.toggleChildrenFields(choosenTaxClaim);
},
triggerChildrenFieldsEvent: function(event){
calculator.toggleChildrenFields($(event.currentTarget).val());
},
updateChildrenFields: function () {
var numStartingChildren = calculator.childrenCountInput.val(),
childFields = calculator.childrenContainer.find('> div.child'),
toggleChildrenFields: function(choosen){
if (choosen == "yes") {
if (calculator.childrenContainer().length == 0) {
var childrenTag = calculator.childrenContainerTemplate.clone();
calculator.taxClaimContainer.append(childrenTag);
childrenTag.attr("id", "children").show();
calculator.partYearChildrenCountInput().on('change', calculator.updateChildrenFields);
calculator.updateChildrenFields();
}
} else {
calculator.partYearChildrenCountInput().off('change');
calculator.childrenContainer().remove();
}
},
updateChildrenFields: function() {
var numStartingChildren = calculator.partYearChildrenCountInput().val(),
childFields = calculator.childrenContainer().find("> div.child"),
numChildFields = childFields.size(),
numNewFields = numStartingChildren - numChildFields;

if (numStartingChildren < 1 || numStartingChildren > 10) {
return false;
}
Expand All @@ -31,7 +59,7 @@
}
}
},
appendChildField: function (index) {
appendChildField: function(index) {
var newChild = calculator.childFieldToClone().clone();

newChild.find('.child-number').text(index+1);
Expand All @@ -44,14 +72,14 @@
$(this).attr('for', calculator.replaceIndex(index, $(this).attr('for')));
});

newChild.appendTo(calculator.childrenContainer);
newChild.appendTo(calculator.childrenContainer());
},
childFieldToClone: function () {
childFieldToClone: function() {
// Always clone the first field so that we don't have to guess
// the index (it will always be zero)
return calculator.childrenContainer.find("> div.child").first();
return calculator.childrenContainer().find("> div.child").first();
},
replaceIndex: function (index, str) {
replaceIndex: function(index, str) {
return str.replace("0", index);
}
};
Expand Down
1 change: 1 addition & 0 deletions app/assets/stylesheets/calculators/child_benefit.scss
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ li {
// scss-lint:disable IdSelector
#children {
margin-bottom: 0;
float: left;

h3 {
@include bold-19;
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/child_benefit_tax_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class ChildBenefitTaxController < ApplicationController
before_filter :setup_slimmer

CALC_PARAM_KEYS = [:adjusted_net_income, :children_count, :starting_children, :year, :results] +
CALC_PARAM_KEYS = [:adjusted_net_income, :children_count, :starting_children, :year, :results, :part_year_children_count] +
AdjustedNetIncomeCalculator::PARAM_KEYS

def landing
Expand Down
2 changes: 1 addition & 1 deletion app/models/adjusted_net_income_calculator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
class AdjustedNetIncomeCalculator
PARAM_KEYS = [:gross_income, :other_income, :pension_contributions_from_pay,
:retirement_annuities, :cycle_scheme, :childcare, :pensions, :property,
:non_employment_income, :gift_aid_donations, :outgoing_pension_contributions]
:non_employment_income, :gift_aid_donations, :outgoing_pension_contributions, :is_part_year_claim]

def initialize(params)
PARAM_KEYS.each do |key|
Expand Down
44 changes: 36 additions & 8 deletions app/models/child_benefit_tax_calculator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class ChildBenefitTaxCalculator
include ActiveModel::Validations

attr_reader :adjusted_net_income_calculator, :adjusted_net_income, :children_count,
:starting_children, :tax_year
:starting_children, :tax_year, :is_part_year_claim, :part_year_children_count

NET_INCOME_THRESHOLD = 50000
TAX_COMMENCEMENT_DATE = Date.parse('7 Jan 2013')
Expand All @@ -18,15 +18,19 @@ class ChildBenefitTaxCalculator
}

validate :valid_child_dates
validates_presence_of :is_part_year_claim, message: "select part year tax claim"
validates_inclusion_of :tax_year, in: TAX_YEARS.keys.map(&:to_i), message: "select a tax year"
validate :valid_number_of_children
validate :tax_year_contains_at_least_one_child

def initialize(params = {})
@adjusted_net_income_calculator = AdjustedNetIncomeCalculator.new(params)
@adjusted_net_income = calculate_adjusted_net_income(params[:adjusted_net_income])
@children_count = params[:children_count] ? params[:children_count].to_i : 1
@starting_children = process_starting_children(params[:starting_children])
@part_year_children_count = params[:part_year_children_count] ? params[:part_year_children_count].to_i : 0
@is_part_year_claim = params[:is_part_year_claim]
@tax_year = params[:year].to_i
@starting_children = process_starting_children(params[:starting_children])
end

def self.valid_date_params?(params)
Expand All @@ -37,16 +41,22 @@ def valid_date_params?(params)
self.class.valid_date_params?(params)
end

# Return the date of the Monday in the future that is closest to the date supplied.
# If the date supplied is a Monday, do not adjust it.
def monday_on_or_after(date)
date + ((1 - date.wday) % 7)
date.monday? ? date : date.next_week(:monday)
end

def nothing_owed?
@adjusted_net_income < NET_INCOME_THRESHOLD || tax_estimate.abs == 0
end

def has_errors?
errors.any? || starting_children.select { |c| c.errors.any? }.any?
errors.any? || starting_children_errors?
end

def starting_children_errors?
is_part_year_claim == 'yes' && starting_children.select { |c| c.errors.any? }.any?
end

def percent_tax_charge
Expand All @@ -68,7 +78,7 @@ def child_benefit_end_date
end

def can_calculate?
valid? && !has_errors? && @starting_children.any?
valid? && !has_errors?
end

def selected_tax_year
Expand All @@ -81,12 +91,16 @@ def can_estimate?

def benefits_claimed_amount
all_weeks_children = {}
full_year_children = @children_count - @part_year_children_count
(child_benefit_start_date...child_benefit_end_date).each_slice(7) do |week|
monday = monday_on_or_after(week.first)
all_weeks_children[monday] = 0
@starting_children.each do |child|
all_weeks_children[monday] += 1 if eligible?(child, tax_year, monday)
end
full_year_children.times do
all_weeks_children[monday] += 1
end
end
# calculate total for all weeks
total = all_weeks_children.values.inject(0) do |sum, n|
Expand All @@ -102,8 +116,14 @@ def tax_estimate
private

def process_starting_children(children)
if selected_tax_year.present?
number_of_children = @part_year_children_count
else
number_of_children = @children_count
end

[].tap do |ary|
@children_count.times do |n|
number_of_children.times do |n|
if children && children[n.to_s] && valid_date_params?(children[n.to_s][:start])
ary << StartingChild.new(children[n.to_s])
else
Expand All @@ -114,8 +134,10 @@ def process_starting_children(children)
end

def eligible?(child, tax_year, week_start_date)
adjusted_start_date = monday_on_or_after(child.start_date)

eligible_for_tax_year?(child, tax_year) &&
days_include_week?(child.adjusted_start_date, child.benefits_end, week_start_date)
days_include_week?(adjusted_start_date, child.benefits_end, week_start_date)
end

def eligible_for_tax_year?(child, tax_year)
Expand Down Expand Up @@ -171,7 +193,13 @@ def parse_child_date(date)
end

def valid_child_dates
@starting_children.each(&:valid?)
is_part_year_claim == 'yes' && @starting_children.each(&:valid?)
end

def valid_number_of_children
if @is_part_year_claim == 'yes' && (@children_count < @part_year_children_count)
errors.add(:part_year_children_count, "the number of children you're claiming a part year for can't be more than the total number of children you're claiming for")
end
end

def tax_year_contains_at_least_one_child
Expand Down
6 changes: 0 additions & 6 deletions app/models/starting_child.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ def benefits_end
@end_date ? @end_date : tax_years[tax_years.keys.sort.last].last
end

# Return the next Monday if start_date is not nil.
def adjusted_start_date
return nil if start_date.nil?
start_date + (start_date.wday < 1 ? 1 : (1 - start_date.wday) + 7)
end

private

def valid_dates
Expand Down
2 changes: 1 addition & 1 deletion app/views/child_benefit_tax/_starting_child.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
<% end %>
<%= select_date (child ? child.end_date : nil), :prefix => "starting_children[#{index}][stop]",
:prompt => {:day => "Day", :month => "Month", :year => "Year" }, :order => [:day, :month, :year],
:start_year => 2.years.ago(Date.today).year, :end_year => 10.years.since(Date.today).year %>
:start_year => 3.years.ago(Date.today).year, :end_year => 10.years.since(Date.today).year %>
</div>
23 changes: 15 additions & 8 deletions app/views/child_benefit_tax/_starting_children.html.erb
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
<% @calculator.starting_children.each_with_index do |child, index| -%>
<div class="child<% if child.errors.any? %> validation-error<% end -%>">
<% child.errors.each do |key, message| -%>
<p><%= message %></p>
<% end -%>
<h3>Child <span class="child-number"><%= index + 1 %></span></h3>
<%= render "starting_child", :index => index, :child => child %>
<% if @calculator.starting_children.count == 0 %>
<div class="child">
<h3>Child <span class="child-number"> 1 </span></h3>
<%= render "starting_child", index: 0, child: StartingChild.new %>
</div>
<% end -%>
<% else %>
<% @calculator.starting_children.each_with_index do |child, index| -%>
<div class="child<% if child.errors.any? %> validation-error<% end -%>">
<% child.errors.each do |key, message| -%>
<p><%= message %></p>
<% end -%>
<h3>Child <span class="child-number"><%= index + 1 %></span></h3>
<%= render "starting_child", :index => index, :child => child %>
</div>
<% end -%>
<% end %>
57 changes: 39 additions & 18 deletions app/views/child_benefit_tax/main.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,14 @@
<% end -%>

<fieldset>
<%= step(1, "Enter the number of children Child Benefit is claimed for:") %>
<%= step(1, "How many children do you want to claim Child Benefit for?") %>
<label for="children_count" class="visuallyhidden">Number of children</label>
<%= select_tag "children_count", options_for_select((1..10).collect{|n| [n,n]}, @calculator.children_count) %>
<%= submit_tag "Update", :name => "children", :class => "button update-button" %>
</fieldset>

<fieldset id="children">
<%= step(2, "Enter the Child Benefit start and stop dates:") %>
<ul>
<li>the start date is usually when you have a baby, adopt or move in with a new partner and their children</li>
<li>the stop date is usually when a child turns 16 or leaves full-time education</li>
</ul>
<%= render "starting_children" %>
</fieldset>

<fieldset id="tax-year">
<%= step(3, "Choose a tax year:") %>
<%= step(2, "Which tax year are you claiming for?") %>
<p>Tax years run from 6 April to 5 April the following year.</p>
<div class="tax-year<% if @calculator.errors.has_key?(:tax_year) %> validation-error<% end %>">
<% @calculator.errors[:tax_year].each do |message| %>
Expand All @@ -60,30 +51,45 @@
</div>
</fieldset>

<fieldset id="is-part-year-claim">
<%= step(3, "Are you claiming for only a part of the tax year for any of your children?") %>
<div class="is-part-year-claim<% if @calculator.errors.has_key?(:is_part_year_claim) %> validation-error<% end %>">
<% @calculator.errors[:is_part_year_claim].each do |message| %>
<p><%= message %></p>
<% end -%>
<% ["yes", "no"].each do |option| -%>
<label for="is_part_year_claim_<%= option %>" class="selectable">
<%= radio_button_tag "is_part_year_claim", option, (@calculator.is_part_year_claim == option) %>
<%= option.capitalize %>
</label>
<% end -%>
</div>
</fieldset>

<fieldset id="adjusted_income">
<%= step(4, "Enter income details for the tax year (optional):") %>
<%= step(4, "Enter your income details for the tax year (optional):") %>
<ul>
<li>don’t combine your household income</li>
<li>use your partner’s income if it’s higher than yours</li>
<li>you may get some of this information from your P60, P11D, employer or tax adviser</li>
<li>you can get some of this information from your P60, P11D, employer or tax adviser</li>
</ul>
<%= label_tag "gross_income", "Salary before tax" %>
<%= money_input "gross_income", @adjusted_net_income_calculator.gross_income, 'aria-describedby' => 'step-4-description' %>
<%= label_tag "other_income", "Other employment income - eg taxable benefits (like a company car or medical insurance), bonuses" %>
<%= label_tag "other_income", "Other employment income - for example taxable benefits (like a company car or medical insurance) or bonuses" %>
<%= money_input "other_income", @adjusted_net_income_calculator.other_income, 'aria-describedby' => 'step-4-description' %>
<%= label_tag "pension_contributions_from_pay", "Pension contributions deducted from your pay (don't include contributions deducted before tax)" %>
<%= money_input "pension_contributions_from_pay", @adjusted_net_income_calculator.pension_contributions_from_pay, 'aria-describedby' => 'step-4-description' %>
<%= label_tag "retirement_annuities", "Retirement annuity contracts" %>
<%= money_input "retirement_annuities", @adjusted_net_income_calculator.retirement_annuities, 'aria-describedby' => 'step-4-description' %>
<%= label_tag "cycle_scheme", "Cycle scheme" %>
<%= money_input "cycle_scheme", @adjusted_net_income_calculator.cycle_scheme, 'aria-describedby' => 'step-4-description' %>
<%= label_tag "childcare", "Childcare paid directly by your employer - eg childcare vouchers (for the whole year but no more than £55 a week), the value of any workplace nursery places" %>
<%= label_tag "childcare", "Childcare paid directly by your employer - for example childcare vouchers (for the whole year but no more than £55 a week) or the value of any workplace nursery places" %>
<%= money_input "childcare", @adjusted_net_income_calculator.childcare, 'aria-describedby' => 'step-4-description' %>
<%= label_tag "pensions", "Income from pension(s) - eg from a state pension" %>
<%= label_tag "pensions", "Income from pension(s) - for example from a state pension" %>
<%= money_input "pensions", @adjusted_net_income_calculator.pensions, 'aria-describedby' => 'step-4-description' %>
<%= label_tag "property", "Income from property - eg rental income" %>
<%= label_tag "property", "Income from property - for example rental income" %>
<%= money_input "property", @adjusted_net_income_calculator.property, 'aria-describedby' => 'step-4-description' %>
<%= label_tag "non_employment_income", "Other income before tax - eg profits from self-employment, taxable savings, dividends" %>
<%= label_tag "non_employment_income", "Other income before tax - for example profits from self-employment, taxable savings, dividends" %>
<%= money_input "non_employment_income", @adjusted_net_income_calculator.non_employment_income, 'aria-describedby' => 'step-4-description' %>
<%= label_tag "gift_aid_donations", "Gift Aid donations" %>
<%= money_input "gift_aid_donations", @adjusted_net_income_calculator.gift_aid_donations, 'aria-describedby' => 'step-4-description' %>
Expand All @@ -93,6 +99,21 @@

<%= submit_tag "Calculate", :name => "results", :class => "button" %>
<% end %>
<div id="children-template" style="display:none">
<div class="part-year-children-count">
<h2>How many children do you want to claim only a part of the tax year for?<h2>
<label for="part_year_children_count" class="visuallyhidden">Number of part year children</label>
<%= select_tag "part_year_children_count", options_for_select((1..10).collect{|n| [n,n]}, @calculator.part_year_children_count) %>
<%= submit_tag "Update Children", :name => "children", :class => "button update-button" %>
</div>

<h2>Enter the Child Benefit start and stop dates:</h2>
<ul>
<li>you can find the start date on your Child Benefit award notice</li>
<li>the stop date is usually when a child turns 16 or leaves full-time education</li>
</ul>
<%= render "starting_children" %>
</div>

<% if can_haz_results? -%>
<div class="results">
Expand Down
Loading

0 comments on commit 39b3cde

Please sign in to comment.