A tax year model will provide the concrete implementation.
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<number>(() => 42);
};
const f = new TestForm();
- const tr = new TaxReturn(2019);
+ const tr = new TestTaxReturn();
expect(f.getValue(tr, 'line')).toBe(42);
//TYPEERROR:
import TaxReturn from './TaxReturn';
import { NotFoundError } from './Errors';
+class TestTaxReturn extends TaxReturn {
+ get year() { return 2019; }
+ get includeJointPersonForms() { return false; }
+};
+
class ConstantLine<T> extends Line<T> {
private _k: T;
};
test('computed line', () => {
- const tr = new TaxReturn(2019);
+ const tr = new TestTaxReturn();
const l = new ComputedLine<number>(
(taxReturn: TaxReturn): number => {
expect(taxReturn).toBe(tr);
};
};
- const tr = new TaxReturn(2019);
+ const tr = new TestTaxReturn();
tr.addForm(new TestForm());
const l1 = new ReferenceLine(TestForm, '6b');
};
};
- const tr = new TaxReturn(2019);
+ const tr = new TestTaxReturn();
const f = new TestForm();
tr.addForm(f);
tr.addForm(new OtherForm());
'3': new InputLine<Input>('key2', undefined, 'FALLBACK')
};
};
- const tr = new TaxReturn(2019);
+ const tr = new TestTaxReturn();
const f = new TestForm({ 'key': 'value' });
tr.addForm(f);
};
};
- const tr = new TaxReturn(2019);
+ const tr = new TestTaxReturn();
tr.addForm(new FormZ({ 'input': 100 }));
tr.addForm(new FormZ2());
};
};
- const tr = new TaxReturn(2019);
+ const tr = new TestTaxReturn();
tr.addForm(new TestForm());
tr.addForm(new TestForm());
tr.addForm(new TestForm());
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');
});
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');
});
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);
});
test('get non-existent person', () => {
- const tr = new TaxReturn(2019);
+ const tr = new TestTaxReturn();
const self = Person.self('Billy Bob');
tr.addPerson(self);
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);
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();
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([]);
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<any, unknown>[] = [];
- constructor(year: number) {
- this._year = year;
- }
+ abstract get year(): number;
- get year(): number {
- return this._year;
- }
+ abstract get includeJointPersonForms(): boolean;
get forms(): Form<any, unknown>[] {
return [...this._forms];
-import { Person, TaxReturn } from '../core';
+import { Person } from '../core';
import { NotFoundError } from '../core/Errors';
import Form1040, { FilingStatus } from './Form1040';
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,
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,
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,
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',
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,
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,
-import { Person, TaxReturn } from '../core';
+import { Person } from '../core';
import { UnsupportedFeatureError } from '../core/Errors';
import Form1040, { FilingStatus } from './Form1040';
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,
continue;
const p = Person.self('B');
- const tr = new TaxReturn(2019);
+ const tr = new TaxReturn();
const f = new Form1116({
person: p,
incomeCategory: category,
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
}));
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
}));
-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,
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,
-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',
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',
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',
-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 = {
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',
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',
-import { Person, TaxReturn } from '../core';
+import { Person } from '../core';
import W2 from './W2';
import Form1040, { FilingStatus } from './Form1040';
import Form8960 from './Form8960';
import Schedule2 from './Schedule2';
import ScheduleD from './ScheduleD';
+import TaxReturn from './TaxReturn';
describe('net investment income tax', () => {
const filingStatusToResult = {
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',
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',
-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);
-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
}));
];
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
});
-import { Person, TaxReturn } from '../core';
+import { Person } from '../core';
import { NotFoundError } from '../core/Errors';
import Form1040, { FilingStatus } from './Form1040';
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 = {
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);
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);
--- /dev/null
+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;
+ }
+};