Remove the need to self-reference Form['lines'] in Form subclasses.
[ustaxlib.git] / src / fed2019 / Form6251.ts
1 // Copyright 2020 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 { Form, TaxReturn } from '../core';
7 import { AccumulatorLine, ComputedLine, ReferenceLine, UnsupportedLine, sumFormLines } from '../core/Line';
8 import { clampToZero } from '../core/Math';
9
10 import Form1040, { QDCGTaxWorksheet, FilingStatus } from './Form1040';
11 import Form1099INT from './Form1099INT';
12 import Schedule1 from './Schedule1';
13 import Schedule2 from './Schedule2';
14 import Schedule3 from './Schedule3';
15 import ScheduleD, { ScheduleDTaxWorksheet } from './ScheduleD';
16
17 export default class Form6251 extends Form {
18 readonly name = '6251';
19
20 readonly lines = {
21 // Part I
22 '1': new ComputedLine((tr): number => {
23 const f1040 = tr.getForm(Form1040);
24 const l11b = f1040.getValue(tr, '11b');
25 if (l11b > 0)
26 return l11b;
27 return f1040.getValue(tr, '8b') - f1040.getValue(tr, '9') - f1040.getValue(tr, '10');
28 }),
29 '2a': new ComputedLine((tr): number => {
30 // Not supported: Schedule A, line 7.
31 return tr.getForm(Form1040).getValue(tr, '9');
32 }),
33 '2b': new ReferenceLine(Schedule1, '1', 'Tax refund', 0), // Not supported - line 8 SALT.
34 '2c': new UnsupportedLine('Investment interest expense'),
35 '2d': new UnsupportedLine('Depletion'),
36 '2e': new UnsupportedLine('Net operating loss deduction'),
37 '2f': new UnsupportedLine('Alternative tax net operating loss deduction'),
38 '2g': new AccumulatorLine(Form1099INT, '9', 'Interest from specified private activity bonds exempt from the regular tax'),
39 '2h': new UnsupportedLine('Qualified small business stock'),
40 '2i': new UnsupportedLine('Exercise of incentive stock options'),
41 '2j': new UnsupportedLine('Estates and trusts (amount from Schedule K-1 (Form 1041), box 12, code A)'),
42 '2k': new UnsupportedLine('Disposition of property'),
43 '2l': new UnsupportedLine('Depreciation on assets placed in service after 1986'),
44 '2m': new UnsupportedLine('Passive activities'),
45 '2n': new UnsupportedLine('Loss limitations'),
46 '2o': new UnsupportedLine('Circulation costs'),
47 '2p': new UnsupportedLine('Long-term contracts'),
48 '2q': new UnsupportedLine('Mining costs'),
49 '2r': new UnsupportedLine('Research and experimental costs'),
50 '2s': new UnsupportedLine('Income from certain installment sales before January 1, 1987'),
51 '2t': new UnsupportedLine('Intangible drilling costs preference'),
52 '3': new UnsupportedLine('Other adjustments'),
53 '4': new ComputedLine((tr): number => {
54 return sumFormLines(tr, this, ['1', '2a', '2b', '2c', '2d', '2e', '2f', '2g', '2h', '2i', '2j', '2k', '2l', '2m', '2n', '2o', '2p', '2q', '2r', '2s', '2t', '3']);
55 }, 'Alternative minimum taxable income'),
56
57 // Part II
58 '5': new ComputedLine((tr): number => {
59 // [ threshold, exemption ]
60 const exemptions = {
61 [FilingStatus.Single]: [ 510300, 71700 ],
62 [FilingStatus.MarriedFilingJoint]: [ 1020600, 111700 ],
63 [FilingStatus.MarriedFilingSeparate]: [ 510300, 55850 ],
64 };
65 const exemption = exemptions[tr.getForm(Form1040).filingStatus];
66
67 const l4 = this.getValue(tr, '4');
68 if (l4 < exemption[0])
69 return exemption[1];
70
71 // Exemption worksheet:
72 const wl1 = exemption[1];
73 const wl2 = l4;
74 const wl3 = exemption[0];
75 const wl4 = clampToZero(wl2 - wl3);
76 const wl5 = wl4 * 0.25;
77 const wl6 = clampToZero(wl1 - wl5);
78 return wl6;
79 }),
80 '6': new ComputedLine((tr): number => {
81 return clampToZero(this.getValue(tr, '4') - this.getValue(tr, '5'));
82 }),
83 '7': new ComputedLine((tr): number => {
84 // Not supported - Form 2555.
85 // Not supported - Form1040 directly reporting cap gains on line 6.
86
87 const f1040 = tr.getForm(Form1040);
88
89 let part3 = f1040.getValue(tr, '3a') > 0;
90
91 const schedD = tr.findForm(ScheduleD);
92 if (schedD) {
93 const flag = schedD.getValue(tr, '15') > 0 && schedD.getValue(tr, '16') > 0;
94 part3 = part3 || flag;
95 }
96
97 if (part3)
98 return this.getValue(tr, '40');
99
100 return computeAmtTax(f1040.filingStatus, this.getValue(tr, '6'));
101 }),
102 '8': new ReferenceLine(Schedule3, '1', 'Alternative minimum tax foreign tax credit'), // Not supported - AMT FTC recalculation
103 '9': new ComputedLine((tr): number => {
104 return this.getValue(tr, '7') - this.getValue(tr, '8');
105 }, 'Tentative minimum tax'),
106 '10': new ComputedLine((tr): number => {
107 let value = tr.getForm(Form1040).getValue(tr, '12a');
108 const sched2 = tr.findForm(Schedule2);
109 if (sched2) {
110 value += sched2.getValue(tr, '2');
111 }
112 // Not supported - subtracting Schedule3@1 for the AMT FTC.
113 return value;
114 }),
115 '11': new ComputedLine((tr): number => {
116 return clampToZero(this.getValue(tr, '9') - this.getValue(tr, '10'));
117 }, 'AMT'),
118
119 // Part III
120 '12': new ReferenceLine(Form6251 as any, '6'),
121 '13': new ComputedLine((tr): number => {
122 const schedDTW = tr.findForm(ScheduleDTaxWorksheet);
123 if (schedDTW)
124 return schedDTW.getValue(tr, '13');
125
126 const qdcgtw = tr.getForm(QDCGTaxWorksheet);
127 return qdcgtw.getValue(tr, '6');
128 }),
129 '14': new ReferenceLine(ScheduleD, '19', undefined, 0),
130 '15': new ComputedLine((tr): number => {
131 const value = this.getValue(tr, '13') + this.getValue(tr, '14');
132 const schedDTW = tr.findForm(ScheduleDTaxWorksheet);
133 if (schedDTW)
134 return Math.min(value, schedDTW.getValue(tr, '10'));
135
136 return value;
137 }),
138 '16': new ComputedLine((tr): number => Math.min(this.getValue(tr, '12'), this.getValue(tr, '15'))),
139 '17': new ComputedLine((tr): number => this.getValue(tr, '12') - this.getValue(tr, '16')),
140 '18': new ComputedLine((tr): number => {
141 const fs = tr.getForm(Form1040).filingStatus;
142 return computeAmtTax(fs, this.getValue(tr, '17'));
143 }),
144 '19': new ComputedLine((tr): number => {
145 switch (tr.getForm(Form1040).filingStatus) {
146 case FilingStatus.Single:
147 case FilingStatus.MarriedFilingSeparate:
148 return 39375;
149 case FilingStatus.MarriedFilingJoint:
150 return 78750;
151 }
152 }),
153 '20': new ComputedLine((tr): number => {
154 const schedDTW = tr.findForm(ScheduleDTaxWorksheet);
155 if (schedDTW)
156 return clampToZero(schedDTW.getValue(tr, '14'));
157
158 const qdcgtw = tr.getForm(QDCGTaxWorksheet);
159 return clampToZero(qdcgtw.getValue(tr, '7'));
160 }),
161 '21': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '19') - this.getValue(tr, '20'))),
162 '22': new ComputedLine((tr): number => Math.min(this.getValue(tr, '12'), this.getValue(tr, '13'))),
163 '23': new ComputedLine((tr): number => Math.min(this.getValue(tr, '21'), this.getValue(tr, '22'))),
164 '24': new ComputedLine((tr): number => this.getValue(tr, '22') - this.getValue(tr, '23')),
165 '25': new ComputedLine((tr): number => {
166 switch (tr.getForm(Form1040).filingStatus) {
167 case FilingStatus.Single: return 434550;
168 case FilingStatus.MarriedFilingSeparate: return 244425;
169 case FilingStatus.MarriedFilingJoint: return 488850;
170 }
171 }),
172 '26': new ReferenceLine(Form6251 as any, '21'),
173 '27': new ComputedLine((tr): number => {
174 const schedDTW = tr.findForm(ScheduleDTaxWorksheet);
175 if (schedDTW)
176 return clampToZero(schedDTW.getValue(tr, '21'));
177
178 const qdcgtw = tr.getForm(QDCGTaxWorksheet);
179 return clampToZero(qdcgtw.getValue(tr, '7'));
180 }),
181 '28': new ComputedLine((tr): number => this.getValue(tr, '26') + this.getValue(tr, '27')),
182 '29': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '25') - this.getValue(tr, '28'))),
183 '30': new ComputedLine((tr): number => Math.min(this.getValue(tr, '24'), this.getValue(tr, '29'))),
184 '31': new ComputedLine((tr): number => this.getValue(tr, '30') * 0.15),
185 '32': new ComputedLine((tr): number => this.getValue(tr, '23') + this.getValue(tr, '30')),
186 '33': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '22') - this.getValue(tr, '32'))),
187 '34': new ComputedLine((tr): number => this.getValue(tr, '33') * 0.20),
188 '35': new ComputedLine((tr): number => this.getValue(tr, '17') + this.getValue(tr, '32') + this.getValue(tr, '33')),
189 '36': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '12') - this.getValue(tr, '35'))),
190 '37': new ComputedLine((tr): number => this.getValue(tr, '36') * 0.25),
191 '38': new ComputedLine((tr): number => sumFormLines(tr, this, ['18', '31', '34', '37'])),
192 '39': new ComputedLine((tr): number => {
193 const fs = tr.getForm(Form1040).filingStatus;
194 return computeAmtTax(fs, this.getValue(tr, '12'));
195 }),
196 '40': new ComputedLine((tr): number => Math.min(this.getValue(tr, '38'), this.getValue(tr, '39'))),
197 };
198 };
199
200 function computeAmtTax(filingStatus, amount) {
201 const mfs = filingStatus = FilingStatus.MarriedFilingSeparate;
202 const limit = mfs ? 97400 : 194800;
203 const sub = mfs ? 1948 : 3896;
204
205 if (amount < limit)
206 return amount * 0.26;
207 return (amount * 0.28) - sub;
208 }