Add overlay forms for fed2020.
[ustaxlib.git] / src / fed2020 / Form1040.ts
1 // Copyright 2021 Blue Static <https://www.bluestatic.org>
2 // This program is free software licensed under the GNU General Public License,
3 // version 3.0. The full text of the license can be found in LICENSE.txt.
4 // SPDX-License-Identifier: GPL-3.0-only
5
6 import { TaxReturn } from '../core';
7 import { Line, AccumulatorLine, ComputedLine, ReferenceLine, SymbolicLine, UnsupportedLine, sumFormLines, sumLineOfForms } from '../core/Line';
8 import { UnsupportedFeatureError } from '../core/Errors';
9 import { clampToZero, reduceBySum, undefinedToZero } from '../core/Math';
10
11 import { Form1040 as Form1040_2019, QDCGTaxWorksheet as QDCGTaxWorksheet_2019 } from '../fed2019';
12
13 import { computeTax } from '.';
14 import { Form1099DIV } from '.';
15 import { Form1099INT } from '.';
16 import { Form1099R, Box7Code } from '.';
17 import { Form8606 } from '.';
18 import { Form8959 } from '.';
19 import { Schedule1 } from '.';
20 import { Schedule2 } from '.';
21 import { Schedule3 } from '.';
22 import { ScheduleD } from '.';
23 import { W2 } from '.';
24
25 export default class Form1040 extends Form1040_2019 {
26 adjustedGrossIncome(tr: TaxReturn): number {
27 return this.getValue(tr, '9') - this.getValue(tr, '10c');
28 }
29
30 totalIncome(tr: TaxReturn): number {
31 return sumFormLines(tr, this, ['1', '2b', '3b', '4b', '5b', '6b', '7', '8']);
32 }
33
34 taxableIncome(tr: TaxReturn): number {
35 return clampToZero(this.getValue(tr, '11') - this.getValue(tr, '14'));
36 }
37
38 readonly lines = {
39 '1': new AccumulatorLine(W2, '1', 'Wages, salaries, tips, etc.'),
40 '2a': new ComputedLine((tr): number => {
41 const value = (new AccumulatorLine(Form1099INT, '8')).value(tr) +
42 (new AccumulatorLine(Form1099DIV, '11')).value(tr);
43 return value;
44 }, 'Tax-exempt interest'),
45 '2b': new ComputedLine((tr) => this.taxableInterest(tr), 'Taxable interest'),
46 '3a': new AccumulatorLine(Form1099DIV, '1b', 'Qualified dividends'),
47 '3b': new AccumulatorLine(Form1099DIV, '1a', 'Ordinary dividends'),
48 '4a': new ComputedLine((tr): number => {
49 const f1099Rs = tr.findForms(Form1099R).filter(f => !f.getValue(tr, '7').includes(Box7Code.G));
50 return sumLineOfForms(tr, f1099Rs, '1');
51 }, 'IRA distributions'),
52 '4b': new ComputedLine((tr): number => {
53 const f8606s = tr.findForms(Form8606);
54 return sumLineOfForms(tr, f8606s, '15c') + sumLineOfForms(tr, f8606s, '18');
55 }, 'IRA distributions, taxable amount'),
56 '5a': new UnsupportedLine('Pensions and annuities'),
57 '5b': new UnsupportedLine('Pensions and annuities, taxable amount'),
58 '6a': new UnsupportedLine('Social security benefits'),
59 '6b': new UnsupportedLine('Social security benefits, taxable amount'),
60
61 '7': new ComputedLine((tr) => this.capitalGainOrLoss(tr), 'Capital gain/loss'),
62
63 '8': new ReferenceLine(Schedule1, '9', 'Other income from Schedule 1', 0),
64
65 '9': new ComputedLine((tr) => this.totalIncome(tr), 'Total income'),
66
67 '10a': new ReferenceLine(Schedule1, '22', 'Adjustments to income', 0),
68 '10b': new UnsupportedLine('Charitable contributions if you take the standard deduction'),
69 '10c': new ComputedLine((tr) => sumFormLines(tr, this, ['10a', '10b']), 'Total adjustments to income'),
70
71 '11': new ComputedLine((tr) => this.adjustedGrossIncome(tr), 'Adjusted gross income'),
72
73 '12': new ComputedLine((tr) => this.deduction(tr), 'Deductions'),
74
75 '13': new ComputedLine((tr) => this.qualifiedBusinessIncomeDeduction(tr), 'Qualified business income deduction'),
76
77 '14': new ComputedLine((tr) => sumFormLines(tr, this, ['12', '13'])),
78
79 '15': new ComputedLine((tr) => this.taxableIncome(tr), 'Taxable income'),
80
81 '16': new ComputedLine((tr) => this.tax(tr), 'Tax'),
82
83 '17': new ReferenceLine(Schedule2, '3', 'Additional tax', 0),
84
85 '18': new ComputedLine((tr) => sumFormLines(tr, this, ['16', '17'])),
86
87 '19': new UnsupportedLine('Child tax credit'),
88
89 '20': new ReferenceLine(Schedule3, '7', undefined, 0),
90
91 '21': new ComputedLine((tr) => sumFormLines(tr, this, ['19', '20'])),
92
93 '22': new ComputedLine((tr) => clampToZero(this.getValue(tr, '18') - this.getValue(tr, '21'))),
94
95 '23': new ReferenceLine(Schedule2, '10', undefined, 0),
96
97 '24': new ComputedLine((tr) => sumFormLines(tr, this, ['22', '23']), 'Total tax'),
98
99 '25a': new AccumulatorLine(W2, '2', 'Withholding from W-2'),
100 '25b': new ComputedLine((tr) => {
101 const fedTaxWithheldBoxes = [
102 new AccumulatorLine(Form1099R, '4'),
103 new AccumulatorLine(Form1099DIV, '4'),
104 new AccumulatorLine(Form1099INT, '4'),
105 ];
106 return fedTaxWithheldBoxes.reduce((acc, cur) => acc + cur.value(tr), 0);
107 }, 'Withholding from 1099'),
108 '25c': new ComputedLine((tr) => {
109 const f8959 = tr.findForm(Form8959)
110 if (f8959) {
111 return f8959.getValue(tr, '24');
112 }
113 // K-1@25c too.
114 return 0;
115 }, 'Withholding from other forms'),
116 '25d': new ComputedLine((tr) => sumFormLines(tr, this, ['25a', '25b', '25c']), 'Federal income tax withheld'),
117
118 '26': new UnsupportedLine('Estimated tax payments'),
119
120 '27': new UnsupportedLine('Earned income credit (EIC)'),
121 '28': new UnsupportedLine('Additional child tax credit. Attach Schedule 8812'),
122 '29': new UnsupportedLine('American opportunity credit from Form 8863, line 8'),
123 '30': new UnsupportedLine('Recovery rebate credit'),
124
125 '31': new ReferenceLine(Schedule3, '13', undefined, 0),
126
127 '32': new ComputedLine((tr) => sumFormLines(tr, this, ['27', '28', '29', '30', '31']), 'Total other payments and refundable credits'),
128
129 '33': new ComputedLine((tr) => sumFormLines(tr, this, ['25d', '26', '32']), 'Total payments'),
130
131 '34': new ComputedLine((tr) => clampToZero(this.getValue(tr, '33') - this.getValue(tr, '24')), 'Amount overpaid'),
132
133 '37': new ComputedLine((tr) => clampToZero(this.getValue(tr, '24') - this.getValue(tr, '33')), 'Amount you owe'),
134 }
135 }
136
137 export class QDCGTaxWorksheet extends QDCGTaxWorksheet_2019 {
138 readonly name = 'QDCG Tax Worksheet';
139
140 dividendsAndCapitalGains(tr: TaxReturn): number {
141 return clampToZero(this.getValue(tr, '2') + this.getValue(tr, '3'));
142 }
143
144 taxableIncomeLessDividendsAndCapitalGains(tr: TaxReturn): number {
145 return clampToZero(this.getValue(tr, '1') - this.getValue(tr, '4'));
146 }
147
148 totalTax(tr: TaxReturn): number {
149 return Math.min(this.getValue(tr, '23'), this.getValue(tr, '24'));
150 }
151
152 readonly lines = {
153 '1': new SymbolicLine(Form1040, 'taxableIncome', 'Taxable income'),
154 '2': new ReferenceLine(Form1040, '3a', 'Qualified dividends'),
155 '3': new ComputedLine((tr): number => {
156 const schedD = tr.findForm(ScheduleD);
157 if (schedD)
158 return clampToZero(Math.min(schedD.getValue(tr, '15'), schedD.getValue(tr, '16')));
159 return tr.getForm(Form1040).capitalGainOrLoss(tr);
160 }),
161 '4': new ComputedLine((tr) => this.dividendsAndCapitalGains(tr)),
162 '5': new ComputedLine((tr) => this.taxableIncomeLessDividendsAndCapitalGains(tr)),
163 '6': new ComputedLine((tr): number => {
164 const fs = tr.getForm(Form1040).filingStatus;
165 return tr.constants.capitalGains.rate0MaxIncome[fs];
166 }),
167 '7': new ComputedLine((tr): number => Math.min(this.getValue(tr, '1'), this.getValue(tr, '6'))),
168 '8': new ComputedLine((tr): number => Math.min(this.getValue(tr, '5'), this.getValue(tr, '7'))),
169 '9': new ComputedLine((tr): number => {
170 return this.getValue(tr, '7') - this.getValue(tr, '8');
171 }, 'Amount taxed at 0%'),
172 '10': new ComputedLine((tr): number => Math.min(this.getValue(tr, '1'), this.getValue(tr, '4'))),
173 '11': new ReferenceLine(QDCGTaxWorksheet as any, '9'),
174 '12': new ComputedLine((tr): number => this.getValue(tr, '10') - this.getValue(tr, '11')),
175 '13': new ComputedLine((tr): number => {
176 const fs = tr.getForm(Form1040).filingStatus;
177 return tr.constants.capitalGains.rate15MaxIncome[fs];
178 }),
179 '14': new ComputedLine((tr): number => Math.min(this.getValue(tr, '1'), this.getValue(tr, '13'))),
180 '15': new ComputedLine((tr): number => this.getValue(tr, '5') + this.getValue(tr, '9')),
181 '16': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '14') - this.getValue(tr, '15'))),
182 '17': new ComputedLine((tr): number => Math.min(this.getValue(tr, '12'), this.getValue(tr, '16'))),
183 '18': new ComputedLine((tr): number => {
184 return this.getValue(tr, '17') * 0.15;
185 }, 'Amount taxed at 15%'),
186 '19': new ComputedLine((tr): number => this.getValue(tr, '9') + this.getValue(tr, '17')),
187 '20': new ComputedLine((tr): number => this.getValue(tr, '10') - this.getValue(tr, '19')),
188 '21': new ComputedLine((tr): number => {
189 return this.getValue(tr, '20') * 0.20;
190 }, 'Amount taxed at 20%'),
191 '22': new ComputedLine((tr): number => {
192 return computeTax(this.getValue(tr, '5'), tr);
193 }, 'Tax on line 5'),
194 '23': new ComputedLine((tr) => sumFormLines(tr, this, ['18', '21', '22'])),
195 '24': new ComputedLine((tr): number => {
196 return computeTax(this.getValue(tr, '1'), tr);
197 }, 'Tax on line 1'),
198 '25': new ComputedLine((tr) => this.totalTax(tr), 'Tax on all taxable income')
199 };
200 }