Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate PV for single person with a disabled child, considering mortality of said child. #23

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

// to use Chromium browser in Ubuntu 18.04, in the VSCode terminal,
// (before calling ng test)
// export CHROME_BIN='/snap/bin/chromium'

module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-firefox-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
Expand All @@ -27,7 +32,8 @@ module.exports = function (config) {
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
// browsers: ['Chrome'],
browsers: ['Firefox'],
singleRun: false, // with the original karma.conf.js, was doing multiple runs in random order, but the tests kept restarting on my computer (when "disconnected"?)
// singleRun: true, // so I added these two lines
// random: false, // still doesn't run all tests
Expand Down
94 changes: 58 additions & 36 deletions src/app/benefit.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,38 +280,54 @@ export class BenefitService {
return childBenefitDate
}

applyAssumedBenefitCut(scenario:CalculationScenario, calcYear:CalculationYear){
applyAssumedBenefitCut(scenario: CalculationScenario, calcYear: CalculationYear) {
if (scenario.benefitCutAssumption === true && calcYear.date.getFullYear() >= scenario.benefitCutYear && calcYear.date.getMonth() === 11) {
//If there's a benefit cut assumption...
//...and we've reached the year in question...
//...and it's December (because we only want to apply this cut at the end of the year, given that it's a multiplication to annual sums)

let cutFactor: number = scenario.cutFactor; // pre-calculate and use *= operation to simplify calculations
//Apply cut to sums included in PV calculation
calcYear.annualBenefitSinglePersonAlive *= cutFactor
calcYear.annualBenefitSinglePersonDeceased *= cutFactor
calcYear.annualBenefitBothAlive *= cutFactor
calcYear.annualBenefitOnlyPersonAalive *= cutFactor
calcYear.annualBenefitOnlyPersonBalive *= cutFactor
calcYear.annualBenefitBothDeceased *= cutFactor
//Apply cut to sums included in output table
calcYear.tablePersonAannualRetirementBenefit *= cutFactor
calcYear.tablePersonAannualSpousalBenefit *= cutFactor
calcYear.tablePersonAannualSurvivorBenefit *= cutFactor
calcYear.tablePersonBannualRetirementBenefit *= cutFactor
calcYear.tablePersonBannualSpousalBenefit *= cutFactor
calcYear.tablePersonBannualSurvivorBenefit *= cutFactor
calcYear.tableTotalAnnualChildBenefitsSingleParentAlive *= cutFactor
calcYear.tableTotalAnnualChildBenefitsSingleParentDeceased *= cutFactor
calcYear.tableTotalAnnualChildBenefitsBothParentsAlive *= cutFactor
calcYear.tableTotalAnnualChildBenefitsBothParentsDeceased *= cutFactor
calcYear.tableTotalAnnualChildBenefitsOnlyPersonAalive *= cutFactor
calcYear.tableTotalAnnualChildBenefitsOnlyPersonBalive *= cutFactor
}
//If there's a benefit cut assumption...
//...and we've reached the year in question...
//...and it's December (because we only want to apply this cut at the end of the year, given that it's a multiplication to annual sums)

let cutFactor: number = scenario.cutFactor; // pre-calculate and use *= operation to simplify calculations
//Apply cut to sums included in PV calculation
if (scenario.maritalStatus == "single") {
if (scenario.disabledChild) {
calcYear.annualBenefitSinglePersonAliveDisabledChildAlive *= cutFactor
calcYear.annualBenefitSinglePersonAliveDisabledChildDeceased *= cutFactor
calcYear.annualBenefitSinglePersonDeceasedDisabledChildAlive *= cutFactor
calcYear.annualBenefitSinglePersonDeceasedDisabledChildDeceased *= cutFactor
} else {
calcYear.annualBenefitSinglePersonAlive *= cutFactor
calcYear.annualBenefitSinglePersonDeceased *= cutFactor
}
} else { // maritalStatus not "single"
calcYear.annualBenefitBothAlive *= cutFactor
calcYear.annualBenefitOnlyPersonAalive *= cutFactor
calcYear.annualBenefitOnlyPersonBalive *= cutFactor
calcYear.annualBenefitBothDeceased *= cutFactor
//Apply cut to sums included in output table
calcYear.tablePersonAannualSpousalBenefit *= cutFactor
calcYear.tablePersonAannualSurvivorBenefit *= cutFactor
calcYear.tablePersonBannualRetirementBenefit *= cutFactor
calcYear.tablePersonBannualSpousalBenefit *= cutFactor
calcYear.tablePersonBannualSurvivorBenefit *= cutFactor
}
//Apply cut to sum included in output table
calcYear.tablePersonAannualRetirementBenefit *= cutFactor
if (scenario.children.length > 0) {
if (scenario.maritalStatus = "single") {
calcYear.tableTotalAnnualChildBenefitsSingleParentAlive *= cutFactor
calcYear.tableTotalAnnualChildBenefitsSingleParentDeceased *= cutFactor
} else {
calcYear.tableTotalAnnualChildBenefitsBothParentsAlive *= cutFactor
calcYear.tableTotalAnnualChildBenefitsBothParentsDeceased *= cutFactor
calcYear.tableTotalAnnualChildBenefitsOnlyPersonAalive *= cutFactor
calcYear.tableTotalAnnualChildBenefitsOnlyPersonBalive *= cutFactor
}
}
}
}


calculateMonthlyPaymentsSingle(scenario:CalculationScenario, calcYear:CalculationYear, person:Person, personAliveBoolean:boolean){
calculateMonthlyPaymentsSingle(scenario:CalculationScenario, calcYear:CalculationYear, person:Person, personAliveBoolean:boolean,
disabledChildAlive: boolean){
//Reset monthlyPayment fields
person.monthlyRetirementPayment = 0
for (let child of scenario.children){
Expand Down Expand Up @@ -347,8 +363,11 @@ export class BenefitService {
person.monthlyRetirementPayment = person.retirementBenefit
for (let child of scenario.children){
if (child.age < 17.99 || child.isOnDisability === true){//if child is eligible for a benefit...
if (calcYear.date >= child.childBenefitDate){//child gets a benefit if we have reached his/her childBenefitDate
child.monthlyChildPayment = person.PIA * 0.5
if (!child.isOnDisability || (disabledChildAlive === true)) {
// leaves child's monthlyChildPayment at 0 if child is disabled and not alive
if (calcYear.date >= child.childBenefitDate){//child gets a benefit if we have reached his/her childBenefitDate
child.monthlyChildPayment = person.PIA * 0.5
}
}
}
}
Expand All @@ -358,11 +377,14 @@ export class BenefitService {
else {//if we're assuming person is deceased
for (let child of scenario.children){
if (child.age < 17.99 || child.isOnDisability === true){//Use 17.99 as the cutoff because sometimes when child is actually 18 javascript value will be 17.9999999
if (person.eligibleForNonCoveredPension === false){
child.monthlyChildPayment = person.PIA * 0.75
}
else {
child.monthlyChildPayment = person.nonWEP_PIA * 0.75
// TODO: need to check childBenefitDate??
if (!child.isOnDisability || (disabledChildAlive === true)) {
if (person.eligibleForNonCoveredPension === false){
child.monthlyChildPayment = person.PIA * 0.75
}
else {
child.monthlyChildPayment = person.nonWEP_PIA * 0.75
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/app/data model classes/calculationscenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class CalculationScenario {
children:Person[] = []
youngestChildTurns16date:MonthYearDate
disabledChild:boolean = false
disabledChildPerson: Person = undefined // this awkward name avoids bulk revisions to all 'disabledChild' references
benefitCutAssumption: boolean = false
benefitCutYear: number = 2034
benefitCutPercentage: number = 23
Expand Down Expand Up @@ -54,8 +55,10 @@ export class CalculationScenario {
//set disabledChild boolean
this.disabledChild = false
for (let child of childrenArray){
// TODO: make sure no more than one disabled child in the scenario (during entry of properties of children)
if (child.isOnDisability === true){
this.disabledChild = true
this.disabledChildPerson = child
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions src/app/data model classes/calculationyear.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class CalculationYear {


annualWithholdingDuetoSinglePersonEarnings: number
annualWithholdingDuetoSinglePersonEarningsDeceasedDisabledChild: number
annualWithholdingDueToPersonAearningsBothAlive: number
annuannualWithholdingDueToPersonAearningsOnlyAalive:number //calculated the same as the field above. But needs to be a separate total so we can subtract from it separately as amounts get withheld in such scenarios.
annualWithholdingDueToPersonBearningsBothAlive: number
Expand All @@ -48,12 +49,20 @@ export class CalculationYear {
personBoverWithholding: number = 0//These amounts no longer used in monthly PV calc

//Sums for calculating PV (lumps everybody's total benefit amount into one sum, per mortality scenario)
// Single person with no disabled child
annualBenefitSinglePersonAlive: number = 0
annualBenefitSinglePersonDeceased: number = 0
// Single person with a disabled child
annualBenefitSinglePersonAliveDisabledChildAlive: number = 0
annualBenefitSinglePersonAliveDisabledChildDeceased: number = 0
annualBenefitSinglePersonDeceasedDisabledChildAlive: number = 0
annualBenefitSinglePersonDeceasedDisabledChildDeceased: number = 0
// Married couple
annualBenefitBothAlive: number = 0
annualBenefitBothDeceased: number = 0
annualBenefitOnlyPersonAalive: number = 0
annualBenefitOnlyPersonBalive: number = 0

annualPV: number = 0

//person-by-person sums for table output (Assumes any parents are alive -- aside from survivor benefit amounts)
Expand All @@ -64,8 +73,16 @@ export class CalculationYear {
tablePersonBannualSpousalBenefit: number = 0
tablePersonBannualSurvivorBenefit: number = 0
//Note that these mirror the PV-related sums above, because we need a different sum for each case
// (Assumes any children are alive)
// For single person
tableTotalAnnualChildBenefitsSingleParentAlive: number = 0
tableTotalAnnualChildBenefitsSingleParentDeceased: number = 0
// Single person with disabled child - may not need all these choices, since assuming children alive
// tableTotalAnnualChildBenefitsSingleParentAliveDisabledChildAlive: number = 0
// tableTotalAnnualChildBenefitsSingleParentDeceasedDisabledChildAlive: number = 0
// tableTotalAnnualChildBenefitsSingleParentAliveDisabledChildDeceased: number = 0
// tableTotalAnnualChildBenefitsSingleParentDeceasedDisabledChildDeceased: number = 0
// For other than single persn
tableTotalAnnualChildBenefitsBothParentsAlive: number = 0
tableTotalAnnualChildBenefitsBothParentsDeceased: number = 0
tableTotalAnnualChildBenefitsOnlyPersonAalive: number = 0
Expand Down
1 change: 1 addition & 0 deletions src/app/data model classes/person.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class Person {
monthlyChildPayment:number = 0
originalBenefit:number = 0 //"original benefit" in the sense as used in family max application. (That is, this is a number that can change from one month to the next.)
retirementARFcreditingMonths:number = 0 //for earnings test
retirementARFcreditingMonthsDisabledChildDeceased:number = 0 //for earnings test, if there is a disabled child
spousalARFcreditingMonths:number = 0 //for earnings test and for months with child-in-care: (See POMS RS 00615.482)

constructor(id:string){
Expand Down
49 changes: 39 additions & 10 deletions src/app/earningstest.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,32 @@ export class EarningsTestService {
return graceYear
}

applyEarningsTestSingle(scenario:CalculationScenario, person:Person, calcYear:CalculationYear){
// TODO: make version for DisabledChildAlive/DisabledChildDeceased
applyEarningsTestSingle(scenario:CalculationScenario, person:Person, calcYear:CalculationYear,
noDeceasedDisabledChild: boolean = true){
//If it's the beginning of a year, calculate earnings test withholding and determine if this is a grace year
if (calcYear.date.getMonth() == 0){
if (calcYear.date.getMonth() == 0 && noDeceasedDisabledChild === true){
calcYear.annualWithholdingDuetoSinglePersonEarnings = this.calculateWithholding(calcYear.date, person)
// in case with a deceased disabled child, their potential withholding starts out same as other case
calcYear.annualWithholdingDuetoSinglePersonEarningsDeceasedDisabledChild = calcYear.annualWithholdingDuetoSinglePersonEarnings
calcYear.personAgraceYear = this.isGraceYear(person, calcYear.date)
if (calcYear.personAgraceYear === true) {person.hasHadGraceYear = true}
}

if (calcYear.annualWithholdingDuetoSinglePersonEarnings > 0){//If more withholding is necessary...
//If more withholding is necessary...
let moreWithholdingNecessary: boolean;
if (noDeceasedDisabledChild === true) {
moreWithholdingNecessary = calcYear.annualWithholdingDuetoSinglePersonEarnings > 0
} else {
moreWithholdingNecessary = calcYear.annualWithholdingDuetoSinglePersonEarningsDeceasedDisabledChild > 0
}
if (moreWithholdingNecessary) {
if (calcYear.date >= person.retirementBenefitDate //And they've started retirement benefit...
&& !(calcYear.personAgraceYear === true && calcYear.date >= person.quitWorkDate) //And it isn't a nonservice month in grace year...
&& calcYear.date < person.FRA){//And they are younger than FRA...
//count how much is available for withholding
let availableForWithholding:number = person.monthlyRetirementPayment
// child.monthlyChildPayment is 0 if disabledChild is deceased, so we can add all the child payments
for (let child of scenario.children){
availableForWithholding = availableForWithholding + child.monthlyChildPayment
}
Expand All @@ -84,10 +96,17 @@ export class EarningsTestService {
for (let child of scenario.children){
child.monthlyChildPayment = 0
}
//Add to tally of months withheld
person.retirementARFcreditingMonths = person.retirementARFcreditingMonths + 1
//Reduce necessary withholding by amount that was withheld this month
calcYear.annualWithholdingDuetoSinglePersonEarnings = calcYear.annualWithholdingDuetoSinglePersonEarnings - availableForWithholding
if (noDeceasedDisabledChild === true) {
// Increment tally of months withheld
person.retirementARFcreditingMonths++
//Reduce necessary withholding by amount that was withheld this month
calcYear.annualWithholdingDuetoSinglePersonEarnings -= availableForWithholding
} else {
// Increment tally of months withheld
person.retirementARFcreditingMonthsDisabledChildDeceased++
//Reduce necessary withholding by amount that was withheld this month
calcYear.annualWithholdingDuetoSinglePersonEarningsDeceasedDisabledChild -= availableForWithholding
}
}
}
}
Expand Down Expand Up @@ -236,11 +255,21 @@ export class EarningsTestService {
}


addBackOverwithholding(calcYear:CalculationYear, scenario:CalculationScenario){
addBackOverwithholding(calcYear:CalculationYear, scenario:CalculationScenario,
disabledChildAlive: boolean = true){
if (scenario.maritalStatus == "single"){
if (calcYear.annualWithholdingDuetoSinglePersonEarnings < 0) {//If annualWithholding is negative due to overwithholding...
calcYear.annualBenefitSinglePersonAlive = calcYear.annualBenefitSinglePersonAlive - calcYear.annualWithholdingDuetoSinglePersonEarnings//add back for PV-related sum
calcYear.tablePersonAannualRetirementBenefit = calcYear.tablePersonAannualRetirementBenefit - calcYear.annualWithholdingDuetoSinglePersonEarnings//add back for table-related sum
if (scenario.disabledChild === true) {
if (disabledChildAlive === true) {
calcYear.annualBenefitSinglePersonAliveDisabledChildAlive -= calcYear.annualWithholdingDuetoSinglePersonEarnings
calcYear.tablePersonAannualRetirementBenefit -= calcYear.annualWithholdingDuetoSinglePersonEarnings//add back for table-related sum
} else {
calcYear.annualBenefitSinglePersonAliveDisabledChildDeceased -= calcYear.annualWithholdingDuetoSinglePersonEarnings
}
} else {
calcYear.annualBenefitSinglePersonAlive -= calcYear.annualWithholdingDuetoSinglePersonEarnings//add back for PV-related sum
calcYear.tablePersonAannualRetirementBenefit -= calcYear.annualWithholdingDuetoSinglePersonEarnings//add back for table-related sum
}
}
}
else {
Expand Down
5 changes: 3 additions & 2 deletions src/app/familymaximum.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,11 @@ export class FamilyMaximumService {
return PIA
}

applyFamilyMaximumSingle(scenario:CalculationScenario, amountLeftForRestOfFamiliy:number){
applyFamilyMaximumSingle(scenario:CalculationScenario, amountLeftForRestOfFamiliy:number,
disabledChildAlive: boolean = true){
let numberOfAxilliaries:number = 0
for (let child of scenario.children){
if (child.isOnDisability === true || child.age < 17.99){
if ((child.isOnDisability === true && (disabledChildAlive === true)) || child.age < 17.99){
numberOfAxilliaries = numberOfAxilliaries + 1
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/app/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,8 @@ export class HomeComponent implements OnInit {
if (this.personBfixedRetirementBenefitMonth && this.personBfixedRetirementBenefitYear){
this.personB.fixedRetirementBenefitDate = new MonthYearDate(this.personBfixedRetirementBenefitYear, this.personBfixedRetirementBenefitMonth-1)
}
this.personA.mortalityTable = this.mortalityService.determineMortalityTable(this.personAgender, this.personAmortalityInput, this.personAassumedDeathAge)
this.personB.mortalityTable = this.mortalityService.determineMortalityTable(this.personBgender, this.personBmortalityInput, this.personBassumedDeathAge)
this.mortalityService.determineMortalityTable(this.personA, this.personAgender, this.personAmortalityInput, this.personAassumedDeathAge)
this.mortalityService.determineMortalityTable(this.personB, this.personBgender, this.personBmortalityInput, this.personBassumedDeathAge)
this.personA.baseMortalityFactor = this.mortalityService.calculateBaseMortalityFactor(this.personA)
this.personB.baseMortalityFactor = this.mortalityService.calculateBaseMortalityFactor(this.personB)

Expand Down
Loading