Implement the Schedule D Tax Worksheet.
[ustaxlib.git] / src / fed2019 / ScheduleD.ts
1 import Form from '../Form';
2 import Person from '../Person';
3 import TaxReturn from '../TaxReturn';
4 import { Line, AccumulatorLine, ComputedLine, ReferenceLine, sumLineOfForms } from '../Line';
5 import { clampToZero } from '../Math';
6 import { UnsupportedFeatureError } from '../Errors';
7
8 import Form8949, { Form8949Box } from './Form8949';
9 import Form1099DIV from './Form1099DIV';
10 import Form1040, { FilingStatus, computeTax } from './Form1040';
11
12 export default class ScheduleD extends Form<ScheduleD['_lines']> {
13 readonly name = 'Schedule D';
14
15 protected readonly _lines = {
16 // 1a not supported (Totals for all short-term transactions reported on Form 1099-B for which basis was reported to the IRS and for which you have no adjustments)
17 // 4 is not supported (Short-term gain from Form 6252 and short-term gain or (loss) from Forms 4684, 6781, and 8824)
18 // 5 is not supported (Net short-term gain or (loss) from partnerships, S corporations, estates, and trusts from Schedule(s) K-1)
19 // 6 is not supported (Short-term capital loss carryover. Enter the amount, if any, from line 8 of your Capital Loss Carryover Worksheet in the instructions)
20
21 '7': new ComputedLine((tr: TaxReturn): number => {
22 // 1-3 are computed by Form8949.
23 // 4-6 should be included.
24 const f8949 = tr.getForm(Form8949);
25 return f8949.getValue(tr, 'boxA').gainOrLoss +
26 f8949.getValue(tr, 'boxB').gainOrLoss +
27 f8949.getValue(tr, 'boxC').gainOrLoss;
28 }, 'Net short-term capital gain or (loss)'),
29
30 // 8a is not supported.
31
32 // 11 is not supported (Gain from Form 4797, Part I; long-term gain from Forms 2439 and 6252; and long-term gain or (loss) from Forms 4684, 6781, and 8824)
33 // 12 is not supported (Net long-term gain or (loss) from partnerships, S corporations, estates, and trusts from Schedule(s) K-1)
34
35 '13': new AccumulatorLine(Form1099DIV, '2a', 'Capital gain distributions'),
36
37 // 14 is not supported (Long-term capital loss carryover. Enter the amount, if any, from line 13 of your Capital Loss Carryover Worksheet in the instructions)
38
39 '15': new ComputedLine((tr: TaxReturn): number => {
40 // 11-14 should be included.
41 const f8949 = tr.getForm(Form8949);
42 return f8949.getValue(tr, 'boxD').gainOrLoss +
43 f8949.getValue(tr, 'boxE').gainOrLoss +
44 f8949.getValue(tr, 'boxF').gainOrLoss +
45 this.getValue(tr, '13');
46 }, 'Net long-term capital gain or (loss)'),
47
48 '16': new ComputedLine((tr: TaxReturn): number => {
49 return this.getValue(tr, '7') + this.getValue(tr, '15');
50 }),
51
52 '17': new ComputedLine((tr: TaxReturn): boolean => {
53 return this.getValue(tr, '15') > 0 && this.getValue(tr, '16') > 0;
54 }, 'Both ST and LT are gains'),
55
56 '18': new ComputedLine((tr: TaxReturn): number | undefined => {
57 if (!this.getValue(tr, '17') || this.getValue(tr, '16') <= 0)
58 return undefined;
59 // Not supported - only for gains on Qualified Small Business Stock or collectibles.
60 return 0;
61 }, '28% Rate Gain Worksheet Value'),
62
63 // 19 is not supported (Unrecaptured Section 1250 Gain Worksheet)
64
65 '20': new ComputedLine((tr: TaxReturn): boolean | undefined => {
66 if (!this.getValue(tr, '17') || this.getValue(tr, '16') <= 0)
67 return undefined;
68 const l18 = this.getValue(tr, '18');
69 const l19 = undefined; //this.getValue(tr, '19');
70 return (l18 === 0 || l18 === undefined) || (l19 === 0 || l19 === undefined);
71 }, 'Line 18 and 19 both 0 or blank?'),
72
73 '21': new ComputedLine((tr: TaxReturn): number | undefined => {
74 if (!this.getValue(tr, '17') || !this.getValue(tr, '20'))
75 return undefined;
76 const filingStatus = tr.getForm(Form1040).getInput('filingStatus');
77 const limit = filingStatus == FilingStatus.MarriedFilingSeparate ? -1500 : -3000;
78 return Math.min(this.getValue(tr, '16'), limit);
79 }, 'Net capital loss'),
80 };
81 };
82
83 export class ScheduleDTaxWorksheet extends Form<ScheduleDTaxWorksheet['_lines']> {
84 readonly name = 'Schedule D Tax Worksheet';
85
86 protected readonly _lines = {
87 '1': new ReferenceLine(Form1040, '11b'),
88 '2': new ReferenceLine(Form1040, '3a'),
89 // TODO 3 - form 4952
90 // TODO 4 - 4952
91 '5': new ComputedLine((tr: TaxReturn): number => 0),
92 '6': new ComputedLine((tr: TaxReturn): number => clampToZero(this.getValue(tr, '2') - this.getValue(tr, '5'))),
93 '7': new ComputedLine((tr: TaxReturn): number => {
94 const schedD = tr.getForm(ScheduleD);
95 return Math.min(schedD.getValue(tr, '15'), schedD.getValue(tr, '16'));
96 }),
97 '8': new ComputedLine((tr: TaxReturn): number => {
98 return 0;
99 // return Math.min(this.getValue(tr, '3'), this.getValue(tr, '4'));
100 }),
101 '9': new ComputedLine((tr: TaxReturn): number => clampToZero(this.getValue(tr, '7') - this.getValue(tr, '8'))),
102 '10': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '6') + this.getValue(tr, '9')),
103 '11': new ComputedLine((tr: TaxReturn): number => {
104 const schedD = tr.getForm(ScheduleD);
105 // TODO - line 19 is not supported.
106 return Math.min(schedD.getValue(tr, '18'), Infinity); //schedD.getValue(tr, '19'));
107 }),
108 '12': new ComputedLine((tr: TaxReturn): number => Math.min(this.getValue(tr, '9'), this.getValue(tr, '11'))),
109 '13': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '10') - this.getValue(tr, '12')),
110 '14': new ComputedLine((tr: TaxReturn): number => clampToZero(this.getValue(tr, '1') - this.getValue(tr, '13'))),
111 '15': new ComputedLine((tr: TaxReturn): number => {
112 switch (tr.getForm(Form1040).getInput('filingStatus')) {
113 case FilingStatus.Single:
114 case FilingStatus.MarriedFilingSeparate:
115 return 39375;
116 case FilingStatus.MarriedFilingJoint:
117 return 78750;
118 }
119 }),
120 '16': new ComputedLine((tr: TaxReturn): number => Math.min(this.getValue(tr, '1'), this.getValue(tr, '15'))),
121 '17': new ComputedLine((tr: TaxReturn): number => Math.min(this.getValue(tr, '14'), this.getValue(tr, '16'))),
122 '18': new ComputedLine((tr: TaxReturn): number => clampToZero(this.getValue(tr, '1') - this.getValue(tr, '10'))),
123 '19': new ComputedLine((tr: TaxReturn): number => {
124 let threshold: number;
125 switch (tr.getForm(Form1040).getInput('filingStatus')) {
126 case FilingStatus.Single:
127 case FilingStatus.MarriedFilingSeparate:
128 threshold = 160725;
129 break;
130 case FilingStatus.MarriedFilingJoint:
131 threshold = 321450;
132 break;
133 }
134 return Math.min(this.getValue(tr, '1'), threshold);
135 }),
136 '20': new ComputedLine((tr: TaxReturn): number => Math.min(this.getValue(tr, '14'), this.getValue(tr, '19'))),
137 '21': new ComputedLine((tr: TaxReturn): number => Math.max(this.getValue(tr, '18'), this.getValue(tr, '20'))),
138 '22': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '16') - this.getValue(tr, '17')),
139 '23': new ComputedLine((tr: TaxReturn): number => Math.min(this.getValue(tr, '1'), this.getValue(tr, '13'))),
140 '24': new ReferenceLine(ScheduleDTaxWorksheet as any, '22'),
141 '25': new ComputedLine((tr: TaxReturn): number => clampToZero(this.getValue(tr, '23') - this.getValue(tr, '24'))),
142 '26': new ComputedLine((tr: TaxReturn): number => {
143 switch (tr.getForm(Form1040).getInput('filingStatus')) {
144 case FilingStatus.Single:
145 return 434550;
146 case FilingStatus.MarriedFilingSeparate:
147 return 244425;
148 case FilingStatus.MarriedFilingJoint:
149 return 488850;
150 }
151 }),
152 '27': new ComputedLine((tr: TaxReturn): number => Math.min(this.getValue(tr, '1'), this.getValue(tr, '26'))),
153 '28': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '21') + this.getValue(tr, '22')),
154 '29': new ComputedLine((tr: TaxReturn): number => clampToZero(this.getValue(tr, '27') - this.getValue(tr, '28'))),
155 '30': new ComputedLine((tr: TaxReturn): number => Math.min(this.getValue(tr, '25'), this.getValue(tr, '29'))),
156 '31': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '30') * 0.15),
157 '32': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '24') + this.getValue(tr, '30')),
158 '33': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '23') - this.getValue(tr, '32')),
159 '34': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '33') * 0.20),
160 '35': new ComputedLine((tr: TaxReturn): number => {
161 const schedD = tr.getForm(ScheduleD);
162 // TODO - line 19 is not supported.
163 return Math.min(this.getValue(tr, '9'), Infinity); //schedD.getValue(tr, '19'));
164 }),
165 '36': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '10') + this.getValue(tr, '21')),
166 '37': new ReferenceLine(ScheduleDTaxWorksheet as any, '1'),
167 '38': new ComputedLine((tr: TaxReturn): number => clampToZero(this.getValue(tr, '36') - this.getValue(tr, '37'))),
168 '39': new ComputedLine((tr: TaxReturn): number => clampToZero(this.getValue(tr, '35') - this.getValue(tr, '38'))),
169 '40': new ComputedLine((tr: TaxReturn): number => this.getValue(tr, '39') * 0.25),
170 '41': new ComputedLine((tr: TaxReturn): number => {
171 const schedD = tr.getForm(ScheduleD);
172 if (schedD.getValue(tr, '18'))
173 throw new UnsupportedFeatureError('28% Gain unsupported');
174 return 0;
175 }),
176 '42': new ComputedLine((tr: TaxReturn): number => {
177 if (!tr.getForm(ScheduleD).getValue(tr, '18'))
178 return 0;
179 return this.getValue(tr, '1') - this.getValue(tr, '41');
180 }),
181 '43': new ComputedLine((tr: TaxReturn): number => {
182 if (!tr.getForm(ScheduleD).getValue(tr, '18'))
183 return 0;
184 return this.getValue(tr, '42') * 0.28;
185 }),
186 '44': new ComputedLine((tr: TaxReturn): number => {
187 const income = this.getValue(tr, '21');
188 return computeTax(income, tr.getForm(Form1040).getInput('filingStatus'));
189 }),
190 '45': new ComputedLine((tr: TaxReturn): number => {
191 return this.getValue(tr, '31') +
192 this.getValue(tr, '34') +
193 this.getValue(tr, '40') +
194 this.getValue(tr, '43') +
195 this.getValue(tr, '44');
196 }),
197 '46': new ComputedLine((tr: TaxReturn): number => {
198 const income = this.getValue(tr, '1');
199 return computeTax(income, tr.getForm(Form1040).getInput('filingStatus'));
200 }),
201 '47': new ComputedLine((tr: TaxReturn): number => Math.min(this.getValue(tr, '45'), this.getValue(tr, '46'))),
202 };
203 };