1 import Form, { FormClass } from '../Form';
2 import TaxReturn from '../TaxReturn';
3 import { Line, AccumulatorLine, ComputedLine, ReferenceLine } from '../Line';
4 import { UnsupportedFeatureError } from '../Errors';
6 import Form8959 from './Form8959';
7 import Form1099INT from './Form1099INT';
8 import Form1099DIV from './Form1099DIV';
9 import FormW2 from './FormW2';
11 export enum FilingStatus {
13 MarriedFilingSeparate,
17 export interface Form1040Input {
18 filingStatus: FilingStatus;
21 const reduceBySum = (list: number[]) => list.reduce((acc, curr) => acc + curr, 0);
23 export default class Form1040 extends Form<Form1040['_lines'], Form1040Input> {
24 readonly name = '1040';
26 protected readonly _lines = {
27 '1': new AccumulatorLine(FormW2, '1', 'Wages, salaries, tips, etc.'),
28 '2a': new AccumulatorLine(Form1099INT, '8', 'Tax-exempt interest'),
29 '2b': new AccumulatorLine(Form1099INT, '1', 'Taxable interest'),
30 '3a': new AccumulatorLine(Form1099DIV, '1b', 'Qualified dividends'),
31 '3b': new AccumulatorLine(Form1099DIV, '1a', 'Ordinary dividends'),
32 // 4a and 4b are complex
33 '4b': new ComputedLine(() => 0),
34 '4d': new ComputedLine(() => 0),
35 // 4c and 4d are not supported
36 // 5a and 5b are not supported
37 '6': new ReferenceLine(/*'Schedule D'*/ undefined, '21', 'Capital gain/loss', 0),
38 '7a': new ReferenceLine(/*'Schedule 1'*/ undefined, '9', 'Other income from Schedule 1', 0),
40 '7b': new ComputedLine((tr: TaxReturn): number => {
42 income += this.getValue(tr, '1');
43 income += this.getValue(tr, '2b');
44 income += this.getValue(tr, '3b');
45 income += this.getValue(tr, '4b');
46 income += this.getValue(tr, '4d');
47 //income += this.getValue(tr, '5b');
48 income += this.getValue(tr, '6');
49 income += this.getValue(tr, '7a');
53 '8a': new ReferenceLine(undefined /*'Schedule 1'*/, '22', 'Adjustments to income', 0),
55 '8b': new ComputedLine((tr: TaxReturn): number => {
56 return this.getValue(tr, '7b') - this.getValue(tr, '8a');
57 }, 'Adjusted gross income'),
60 '9': new ComputedLine(() => 0, 'Deduction'),
62 '10': new ComputedLine((tr: TaxReturn): number => {
63 const taxableIncome = this.getValue(tr, '8b');
65 switch (this.getInput('filingStatus')) {
66 case FilingStatus.Single: use8995a = taxableIncome <= 160700; break;
67 case FilingStatus.MarriedFilingSeparate: use8995a = taxableIncome <= 160725; break;
68 case FilingStatus.MarriedFilingJoint: use8995a = taxableIncome <= 321400; break;
71 }, 'Qualified business income deduction'),
73 '11a': new ComputedLine((tr: TaxReturn): number => {
74 return this.getValue(tr, '9') + this.getValue(tr, '10');
76 '11b': new ComputedLine((tr: TaxReturn): number => {
77 const value = this.getValue(tr, '8b') - this.getValue(tr, '11a');
78 return value < 0 ? 0 : value;
81 '12a': new ComputedLine((tr: TaxReturn): number => {
83 // Form 8814 (election to report child's interest or dividends)
84 // Form 4972 (relating to lump-sum distributions)
85 const taxableIncome = this.getValue(tr, '11b');
86 if (taxableIncome < 100000)
87 throw new UnsupportedFeatureError('Tax-table tax liability not supported');
89 const l11b = this.getValue(tr, '11b');
91 switch (this.getInput('filingStatus')) {
92 case FilingStatus.Single:
93 if (taxableIncome < 160725)
94 return (l11b * 0.24) - 5825.50;
95 else if (taxableIncome < 204100)
96 return (l11b * 0.32) - 18683.50;
97 else if (taxableIncome < 510300)
98 return (l11b * 0.35) - 24806.50;
100 return (l11b * 0.38) - 35012.50;
101 case FilingStatus.MarriedFilingJoint:
102 if (taxableIncome < 168400)
103 return (l11b * 0.22) - 8283.00;
104 else if (taxableIncome < 321450)
105 return (l11b * 0.24) - 11651.00;
106 else if (taxableIncome < 408200)
107 return (l11b * 0.32) - 37367.00;
108 else if (taxableIncome < 612350)
109 return (l11b * 0.35) - 49613.00;
111 return (l11b * 0.37) - 61860.00;
112 case FilingStatus.MarriedFilingSeparate:
113 if (taxableIncome < 160725)
114 return (l11b * 0.24) - 5825.50;
115 else if (taxableIncome < 204100)
116 return (l11b * 0.32) - 18683.50;
117 else if (taxableIncome < 306175)
118 return (l11b * 0.35) - 24806.50;
120 return (l11b * 0.37) - 30930.00;
122 throw new UnsupportedFeatureError('Unexpected return type');
125 '12b': new ComputedLine((tr: TaxReturn): number => {
126 return this.getValue(tr, '12a') + tr.getForm(Schedule2).getValue(tr, '3');
127 }, 'Additional tax'),
129 // Not supported: 13a - child tax credit
131 '13b': new ComputedLine((tr: TaxReturn): number => {
132 // TODO: add Sched 3.L7
134 }, 'Additional credits'),
136 '14': new ComputedLine((tr: TaxReturn): number => {
137 const l12b = this.getValue(tr, '12b');
138 const l13b = this.getValue(tr, '13b');
139 const value = l12b - l13b;
140 return value < 0 ? 0 : value;
143 '15': new ReferenceLine(undefined /*'Schedule 2'*/, '10', undefined, 0),
145 '16': new ComputedLine((tr: TaxReturn): number => {
146 return this.getValue(tr, '14') + this.getValue(tr, '15');
149 '17': new ComputedLine((tr: TaxReturn): number => {
150 const fedTaxWithheldBoxes = [
151 new AccumulatorLine(FormW2, '2'),
152 //new AccumulatorLine(Form1099R, '4'),
153 new AccumulatorLine(Form1099DIV, '4'),
154 new AccumulatorLine(Form1099INT, '4'),
156 const withholding: number[] = fedTaxWithheldBoxes.map(b => b.value(tr));
158 let additionalMedicare = 0;
159 const f8959 = tr.findForm(Form8959)
161 additionalMedicare = f8959.getValue(tr, '24');
164 return reduceBySum(withholding) + additionalMedicare;
165 }, 'Federal income tax withheld'),
169 '19': new ReferenceLine(Form1040 as any, '17', 'Total payments'),
171 '20': new ComputedLine((tr: TaxReturn): number => {
172 const l16: number = this.getValue(tr, '16');
173 const l19: number = this.getValue(tr, '19');
177 }, 'Amount overpaid'),
179 '23': new ComputedLine((tr: TaxReturn): number => {
180 const l16 = this.getValue(tr, '16');
181 const l19 = this.getValue(tr, '19');
185 }, 'Amount you owe'),
189 export class Schedule2 extends Form<Schedule2['_lines']> {
190 readonly name = 'Schedule 2';
192 protected readonly _lines = {
193 '1': new ComputedLine((tr: TaxReturn): number => {
194 // TODO - this is just using Taxable Income, rather than AMT-limited
196 const f1040 = tr.getForm(Form1040);
197 const taxableIncome = f1040.getValue(tr, '11b');
198 switch (f1040.getInput('filingStatus')) {
199 case FilingStatus.Single:
200 if (taxableIncome < 510300)
202 case FilingStatus.MarriedFilingJoint:
203 if (taxableIncome < 1020600)
205 case FilingStatus.MarriedFilingSeparate:
206 if (taxableIncome < 510300)
209 throw new UnsupportedFeatureError('The AMT is not supported');
211 // 2 is not supported (Excess advance premium tax credit repayment)
212 '3': new ComputedLine((tr: TaxReturn): number => {
213 // Should include line 2.
214 return this.getValue(tr, '1');
217 // 4 is not supported (Self-employment tax.)
218 // 5 is not supported (Unreported social security and Medicare tax from)
219 // 6 is not supported (Additional tax on IRAs, other qualified retirement plans, and other tax-favored accounts)
220 // 7 is not supported (Household employment taxes.)
221 '8': new ComputedLine((tr: TaxReturn): number => {
222 const f1040 = tr.getForm(Form1040);
223 const wages = f1040.getLine('1').value(tr);
224 const agi = f1040.getLine('8b').value(tr);
227 const filingStatus = f1040.getInput('filingStatus');
229 const additionalMedicare = wages > Form8959.filingStatusLimit(filingStatus);
231 switch (f1040.getInput('filingStatus')) {
232 case FilingStatus.Single:
233 if (wages > 200000) {
237 case FilingStatus.MarriedFilingJoint:
238 if (wages > 250000) {
242 case FilingStatus.MarriedFilingSeparate:
243 if (wages > 125000) {
251 if (additionalMedicare) {
252 const f8959 = tr.getForm(Form8959);
253 value += f8959.getValue(tr, '18');
257 //const f8960 = tr.getForm('8960');
262 // 9 is not supported (Section 965 net tax liability installment from Form 965-A)
264 '10': new ComputedLine((tr: TaxReturn): number => {
265 // Should be lines 4 - 8.
266 return this.getValue(tr, '8');