From 22811b4989338db1296617d28acbd18175606d33 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Fri, 13 Mar 2020 00:32:37 -0400 Subject: [PATCH] Make core/TaxReturn an abstract class. A tax year model will provide the concrete implementation. --- src/core/Form.test.ts | 7 ++++++- src/core/Line.test.ts | 17 +++++++++++------ src/core/TaxReturn.test.ts | 25 +++++++++++++------------ src/core/TaxReturn.ts | 12 +++--------- src/fed2019/Form1040.test.ts | 15 ++++++++------- src/fed2019/Form1116.test.ts | 11 ++++++----- src/fed2019/Form8606.test.ts | 7 ++++--- src/fed2019/Form8949.test.ts | 9 +++++---- src/fed2019/Form8959.test.ts | 7 ++++--- src/fed2019/Form8960.test.ts | 7 ++++--- src/fed2019/FormW2.test.ts | 5 +++-- src/fed2019/Schedule1.test.ts | 7 ++++--- src/fed2019/Schedule3.test.ts | 7 ++++--- src/fed2019/TaxReturn.ts | 11 +++++++++++ 14 files changed, 86 insertions(+), 61 deletions(-) create mode 100644 src/fed2019/TaxReturn.ts diff --git a/src/core/Form.test.ts b/src/core/Form.test.ts index 309c302..9aebe24 100644 --- a/src/core/Form.test.ts +++ b/src/core/Form.test.ts @@ -3,6 +3,11 @@ import TaxReturn from './TaxReturn'; import Form, { isFormT } from './Form'; import { InconsistencyError, NotFoundError } from './Errors'; +class TestTaxReturn extends TaxReturn { + get year() { return 2019; } + get includeJointPersonForms() { return true; } +}; + test('add and get line', () => { const l = new ComputedLine(() => 42); @@ -55,7 +60,7 @@ test('get value', () => { }; const f = new TestForm(); - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); expect(f.getValue(tr, 'line')).toBe(42); //TYPEERROR: diff --git a/src/core/Line.test.ts b/src/core/Line.test.ts index ababbb7..65221f8 100644 --- a/src/core/Line.test.ts +++ b/src/core/Line.test.ts @@ -3,6 +3,11 @@ import Form, { FormClass } from './Form'; import TaxReturn from './TaxReturn'; import { NotFoundError } from './Errors'; +class TestTaxReturn extends TaxReturn { + get year() { return 2019; } + get includeJointPersonForms() { return false; } +}; + class ConstantLine extends Line { private _k: T; @@ -17,7 +22,7 @@ class ConstantLine extends Line { }; test('computed line', () => { - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const l = new ComputedLine( (taxReturn: TaxReturn): number => { expect(taxReturn).toBe(tr); @@ -37,7 +42,7 @@ test('reference line', () => { }; }; - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); tr.addForm(new TestForm()); const l1 = new ReferenceLine(TestForm, '6b'); @@ -71,7 +76,7 @@ test('self reference line', () => { }; }; - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const f = new TestForm(); tr.addForm(f); tr.addForm(new OtherForm()); @@ -95,7 +100,7 @@ test('input line', () => { '3': new InputLine('key2', undefined, 'FALLBACK') }; }; - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const f = new TestForm({ 'key': 'value' }); tr.addForm(f); @@ -125,7 +130,7 @@ test('line stack', () => { }; }; - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); tr.addForm(new FormZ({ 'input': 100 })); tr.addForm(new FormZ2()); @@ -142,7 +147,7 @@ test('accumulator line', () => { }; }; - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); tr.addForm(new TestForm()); tr.addForm(new TestForm()); tr.addForm(new TestForm()); diff --git a/src/core/TaxReturn.test.ts b/src/core/TaxReturn.test.ts index 9a5b252..21ca625 100644 --- a/src/core/TaxReturn.test.ts +++ b/src/core/TaxReturn.test.ts @@ -3,19 +3,20 @@ import Person from './Person'; import Form from './Form'; import { NotFoundError, InconsistencyError } from './Errors'; -test('constructor', () => { - const tr = new TaxReturn(2019); - expect(tr.year).toBe(2019); -}); +class TestTaxReturn extends TaxReturn { + get year() { return 2019; } + + includeJointPersonForms = false; +}; test('does not support Dependents', () => { - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const p = Person.dependent('Baby'); expect(() => tr.addPerson(p)).toThrow('Dependents are not supported'); }); test('add more than one Self', () => { - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const p1 = Person.self('A'); tr.addPerson(p1); const p2 = Person.self('B'); @@ -23,7 +24,7 @@ test('add more than one Self', () => { }); test('add more than one Spouse', () => { - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const p1 = Person.spouse('A'); tr.addPerson(p1); const p2 = Person.spouse('B'); @@ -31,7 +32,7 @@ test('add more than one Spouse', () => { }); test('add Self and Spouse', () => { - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const self = Person.self('Billy Bob'); const spouse = Person.spouse('Jilly Bob'); tr.addPerson(self); @@ -44,7 +45,7 @@ test('add Self and Spouse', () => { }); test('get non-existent person', () => { - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const self = Person.self('Billy Bob'); tr.addPerson(self); @@ -58,7 +59,7 @@ test('single-copy forms', () => { protected readonly _lines = null; }; - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const f = new TestForm(); tr.addForm(f); expect(() => tr.addForm(new TestForm)).toThrow(InconsistencyError); @@ -73,7 +74,7 @@ test('multiple-copy forms', () => { protected readonly _lines = null; }; - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); const f1 = new TestForm(); const f2 = new TestForm(); const f3 = new TestForm(); @@ -95,7 +96,7 @@ test('get non-existent form', () => { readonly name = 'Test Form'; protected readonly _lines = null; } - const tr = new TaxReturn(2019); + const tr = new TestTaxReturn(); expect(() => tr.getForm(TestForm)).toThrow(NotFoundError); expect(tr.findForm(TestForm)).toBeNull(); expect(tr.findForms(TestForm)).toEqual([]); diff --git a/src/core/TaxReturn.ts b/src/core/TaxReturn.ts index 3320756..359acab 100644 --- a/src/core/TaxReturn.ts +++ b/src/core/TaxReturn.ts @@ -2,19 +2,13 @@ import Form, { FormClass, isFormT } from './Form'; import Person, { Relation } from './Person'; import { NotFoundError, InconsistencyError, UnsupportedFeatureError } from './Errors'; -export default class TaxReturn { - private _year: number; - +export default abstract class TaxReturn { private _people: Person[] = []; private _forms: Form[] = []; - constructor(year: number) { - this._year = year; - } + abstract get year(): number; - get year(): number { - return this._year; - } + abstract get includeJointPersonForms(): boolean; get forms(): Form[] { return [...this._forms]; diff --git a/src/fed2019/Form1040.test.ts b/src/fed2019/Form1040.test.ts index d8d567f..12f1ba9 100644 --- a/src/fed2019/Form1040.test.ts +++ b/src/fed2019/Form1040.test.ts @@ -1,4 +1,4 @@ -import { Person, TaxReturn } from '../core'; +import { Person } from '../core'; import { NotFoundError } from '../core/Errors'; import Form1040, { FilingStatus } from './Form1040'; @@ -11,12 +11,13 @@ import ScheduleD, { ScheduleDTaxWorksheet } from './ScheduleD'; import Form8606 from './Form8606'; import Form8959 from './Form8959'; import Form8949 from './Form8949'; +import TaxReturn from './TaxReturn'; import W2 from './W2'; test('w2 wages', () => { const pa = Person.self('A'); const pb = Person.spouse('B'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new W2({ employer: 'AA', employee: pa, @@ -40,7 +41,7 @@ test('w2 wages', () => { test('interest income', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1099INT({ payer: 'Bank', payee: p, @@ -63,7 +64,7 @@ test('interest income', () => { test('dividend income', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); const f1099div = new Form1099DIV({ payer: 'Brokerage', payee: p, @@ -83,7 +84,7 @@ test('dividend income', () => { test('capital gain/loss', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); tr.addForm(new W2({ employer: 'Money', @@ -108,7 +109,7 @@ test('capital gain/loss', () => { test('require Form8959', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new W2({ employer: 'Company', employee: p, @@ -128,7 +129,7 @@ test('require Form8959', () => { test('backdoor and megabackdoor roth', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1099R({ payer: 'Roth', payee: p, diff --git a/src/fed2019/Form1116.test.ts b/src/fed2019/Form1116.test.ts index 2619503..add04cc 100644 --- a/src/fed2019/Form1116.test.ts +++ b/src/fed2019/Form1116.test.ts @@ -1,4 +1,4 @@ -import { Person, TaxReturn } from '../core'; +import { Person } from '../core'; import { UnsupportedFeatureError } from '../core/Errors'; import Form1040, { FilingStatus } from './Form1040'; @@ -7,11 +7,12 @@ import Form1099B, { GainType } from './Form1099B'; import Form1099DIV from './Form1099DIV'; import Form8949 from './Form8949'; import W2 from './W2'; +import TaxReturn from './TaxReturn'; import ScheduleD from './ScheduleD'; test('supported income category', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); const f = new Form1116({ person: p, incomeCategory: ForeignIncomeCategory.C, @@ -29,7 +30,7 @@ test('unsupported income categories', () => { continue; const p = Person.self('B'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); const f = new Form1116({ person: p, incomeCategory: category, @@ -44,7 +45,7 @@ test('unsupported income categories', () => { test('foreign tax credit', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus: FilingStatus.MarriedFilingJoint })); @@ -84,7 +85,7 @@ test('foreign tax credit', () => { test('no net capital losses in total income', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus: FilingStatus.MarriedFilingJoint })); diff --git a/src/fed2019/Form8606.test.ts b/src/fed2019/Form8606.test.ts index b7bd8a2..643c8e4 100644 --- a/src/fed2019/Form8606.test.ts +++ b/src/fed2019/Form8606.test.ts @@ -1,11 +1,12 @@ -import { Person, TaxReturn } from '../core'; +import { Person } from '../core'; import Form1040, { FilingStatus } from './Form1040'; import Form8606 from './Form8606'; +import TaxReturn from './TaxReturn'; test('skip part 1', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); const f = new Form8606({ person: p, nondeductibleContributions: 6000, @@ -19,7 +20,7 @@ test('skip part 1', () => { test('Roth conversion no basis', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); const f = new Form8606({ person: p, nondeductibleContributions: 6000, diff --git a/src/fed2019/Form8949.test.ts b/src/fed2019/Form8949.test.ts index 1fb5e55..4719181 100644 --- a/src/fed2019/Form8949.test.ts +++ b/src/fed2019/Form8949.test.ts @@ -1,14 +1,15 @@ -import { Person, TaxReturn } from '../core'; +import { Person } from '../core'; import Form1040, { FilingStatus } from './Form1040'; import Form1099B, { GainType } from './Form1099B'; import Form8949, { Form8949Box, Form8949Total } from './Form8949'; +import TaxReturn from './TaxReturn'; describe('single form', () => { for (const box of [Form8949Box.A, Form8949Box.B, Form8949Box.D, Form8949Box.E]) { test(`box ${Form8949Box[box]}`, () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); tr.addForm(new Form1099B({ payer: 'Brokerage', @@ -47,7 +48,7 @@ describe('single form', () => { test('multiple forms', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); tr.addForm(new Form1099B({ payer: 'Brokerage', @@ -104,7 +105,7 @@ test('multiple forms', () => { test('adjustments', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); const b1 = new Form1099B({ payer: 'Brokerage', diff --git a/src/fed2019/Form8959.test.ts b/src/fed2019/Form8959.test.ts index 684e7cf..890311b 100644 --- a/src/fed2019/Form8959.test.ts +++ b/src/fed2019/Form8959.test.ts @@ -1,9 +1,10 @@ -import { Person, TaxReturn } from '../core'; +import { Person } from '../core'; import W2 from './W2'; import Form8959 from './Form8959'; import Form1040, { FilingStatus } from './Form1040'; import Schedule2 from './Schedule2'; +import TaxReturn from './TaxReturn'; describe('additional medicare tax', () => { const filingStatusToResults = { @@ -21,7 +22,7 @@ describe('additional medicare tax', () => { for (const filingStatus of Object.values(FilingStatus)) { test(`filing status ${filingStatus}`, () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus })); tr.addForm(new W2({ employer: 'Acme', @@ -56,7 +57,7 @@ describe('no additional medicare tax', () => { for (const filingStatus of Object.values(FilingStatus)) { test(`filing status ${filingStatus}`, () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus })); tr.addForm(new W2({ employer: 'Acme', diff --git a/src/fed2019/Form8960.test.ts b/src/fed2019/Form8960.test.ts index 753af6b..093ae2a 100644 --- a/src/fed2019/Form8960.test.ts +++ b/src/fed2019/Form8960.test.ts @@ -1,4 +1,4 @@ -import { Person, TaxReturn } from '../core'; +import { Person } from '../core'; import W2 from './W2'; import Form1040, { FilingStatus } from './Form1040'; @@ -10,6 +10,7 @@ import Form8959 from './Form8959'; import Form8960 from './Form8960'; import Schedule2 from './Schedule2'; import ScheduleD from './ScheduleD'; +import TaxReturn from './TaxReturn'; describe('net investment income tax', () => { const filingStatusToResult = { @@ -21,7 +22,7 @@ describe('net investment income tax', () => { for (const filingStatus of Object.values(FilingStatus)) { test(`filing status ${filingStatus}`, () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus })); tr.addForm(new Form1099DIV({ payer: 'Brokerage', @@ -81,7 +82,7 @@ describe('no net investment income tax', () => { for (const filingStatus of Object.values(FilingStatus)) { test(`filing status ${filingStatus}`, () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus })); tr.addForm(new Form1099DIV({ payer: 'Brokerage', diff --git a/src/fed2019/FormW2.test.ts b/src/fed2019/FormW2.test.ts index 9505f61..603c129 100644 --- a/src/fed2019/FormW2.test.ts +++ b/src/fed2019/FormW2.test.ts @@ -1,11 +1,12 @@ -import { Person, TaxReturn } from '../core'; +import { Person } from '../core'; +import TaxReturn from './TaxReturn'; import W2 from './W2'; test('input', () => { const p = Person.self('Bob'); const w2 = new W2({ employer: 'Acme', employee: p, wages: 1000, fedIncomeTax: 100.40 }); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(w2); expect(w2.getValue(tr, 'c')).toBe('Acme'); expect(w2.getValue(tr, 'e')).toBe(p); diff --git a/src/fed2019/Schedule1.test.ts b/src/fed2019/Schedule1.test.ts index e99e72c..290a8e1 100644 --- a/src/fed2019/Schedule1.test.ts +++ b/src/fed2019/Schedule1.test.ts @@ -1,12 +1,13 @@ -import { Person, TaxReturn } from '../core'; +import { Person } from '../core'; import { UnsupportedFeatureError } from '../core/Errors'; import Form1040, { FilingStatus } from './Form1040'; import Schedule1, { Schedule1Input } from './Schedule1'; +import TaxReturn from './TaxReturn'; test('state tax refund', () => { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); @@ -33,7 +34,7 @@ test('unsupported inputs', () => { ]; for (const input of keys) { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); const f = new Schedule1({ [input]: 100 }); diff --git a/src/fed2019/Schedule3.test.ts b/src/fed2019/Schedule3.test.ts index e0b5efc..1e7a0ba 100644 --- a/src/fed2019/Schedule3.test.ts +++ b/src/fed2019/Schedule3.test.ts @@ -1,4 +1,4 @@ -import { Person, TaxReturn } from '../core'; +import { Person } from '../core'; import { NotFoundError } from '../core/Errors'; import Form1040, { FilingStatus } from './Form1040'; @@ -7,6 +7,7 @@ import Form1116 from './Form1116'; import Form8949 from './Form8949'; import Schedule3 from './Schedule3'; import ScheduleD from './ScheduleD'; +import TaxReturn from './TaxReturn'; test('foreign tax credit, form 1116 not required', () => { const filingStatusToForeignTax = { @@ -17,7 +18,7 @@ test('foreign tax credit, form 1116 not required', () => { for (const filingStatus of Object.values(FilingStatus)) { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus })); tr.addForm(new Form8949); tr.addForm(new ScheduleD); @@ -45,7 +46,7 @@ test('foreign tax credit, form 1116 required', () => { for (const filingStatus of Object.values(FilingStatus)) { const p = Person.self('A'); - const tr = new TaxReturn(2019); + const tr = new TaxReturn(); tr.addForm(new Form1040({ filingStatus })); tr.addForm(new Form8949); tr.addForm(new ScheduleD); diff --git a/src/fed2019/TaxReturn.ts b/src/fed2019/TaxReturn.ts new file mode 100644 index 0000000..9a7892f --- /dev/null +++ b/src/fed2019/TaxReturn.ts @@ -0,0 +1,11 @@ +import { TaxReturn as BaseTaxReturn } from '../core'; + +import Form1040, { FilingStatus } from './Form1040'; + +export default class TaxReturn extends BaseTaxReturn { + get year() { return 2019; } + + get includeJointPersonForms() { + return this.getForm(Form1040).getInput('filingStatus') == FilingStatus.MarriedFilingJoint; + } +}; -- 2.22.5