Simplify the Trace API further.
[ustaxlib.git] / src / fed2019 / Form8949.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 * as Trace from '../core/Trace';
7 import { Form, Person, TaxReturn } from '../core';
8 import { Line, InputLine, ComputedLine, sumLineOfForms } from '../core/Line';
9
10 import Form1099B, { GainType } from './Form1099B';
11
12 export enum Form8949Box {
13 A = 'A', // Short-term transactions reported on Form(s) 1099-B showing basis was reported to the IRS
14 B = 'B', // Short-term transactions reported on Form(s) 1099-B showing basis wasn’t reported to the IRS
15 C = 'C', // Short-term transactions not reported to you on Form 1099-B
16 D = 'D', // Long-term transactions reported on Form(s) 1099-B showing basis was reported to the IRS
17 E = 'E', // Long-term transactions reported on Form(s) 1099-B showing basis wasn’t reported to the IRS
18 F = 'F', // Long-term transactions not reported to you on Form 1099-B
19 };
20
21 export interface Adjustment {
22 entry: Form1099B;
23 code: string;
24 amount: number;
25 };
26
27 export interface Form8949Input {
28 adjustments?: Adjustment[];
29 };
30
31 export interface Form8949Total {
32 proceeds: number;
33 costBasis: number;
34 adjustments: number;
35 gainOrLoss: number;
36 };
37
38 function matching1099Bs(tr: TaxReturn, box: Form8949Box): Form1099B[] {
39 return tr.findForms(Form1099B).filter(f => {
40 const gainType: GainType = f.getValue(tr, '2');
41 const basisReported: boolean = f.getValue(tr, '12');
42
43 switch (box) {
44 case Form8949Box.A:
45 return gainType == GainType.ShortTerm && basisReported;
46 case Form8949Box.B:
47 return gainType == GainType.ShortTerm && !basisReported;
48 case Form8949Box.D:
49 return gainType == GainType.LongTerm && basisReported;
50 case Form8949Box.E:
51 return gainType == GainType.LongTerm && !basisReported;
52 };
53
54 return false;
55 });
56 }
57
58 class Form8949Line extends Line<Form8949Total> {
59 private _box: Form8949Box;
60 private _line: keyof Form1099B['lines'];
61
62 constructor(box: Form8949Box) {
63 super(`Form 8949 Box ${box} Total`);
64 this._box = box;
65 }
66
67 value(tr: TaxReturn): Form8949Total {
68 Trace.begin(this);
69 const f1099bs = matching1099Bs(tr, this._box);
70 const proceeds = sumLineOfForms(tr, f1099bs, '1d');
71 const costBasis = sumLineOfForms(tr, f1099bs, '1e');
72 const f8949 = tr.getForm(Form8949);
73 const adjustments = !f8949.hasInput('adjustments') ? 0 :
74 f8949.getInput('adjustments')
75 .filter(a => f1099bs.includes(a.entry))
76 .reduce((acc, curr) => acc + curr.amount, 0);
77 Trace.end();
78 return {
79 proceeds,
80 costBasis,
81 adjustments,
82 gainOrLoss: proceeds - costBasis + adjustments,
83 };
84 }
85 };
86
87 export default class Form8949 extends Form<Form8949['_lines'], Form8949Input> {
88 readonly name = '8949';
89
90 readonly supportsMultipleCopies = true;
91
92 protected readonly _lines = {
93 'boxA': new Form8949Line(Form8949Box.A),
94 'boxB': new Form8949Line(Form8949Box.B),
95 'boxC': new Form8949Line(Form8949Box.C),
96 'boxD': new Form8949Line(Form8949Box.D),
97 'boxE': new Form8949Line(Form8949Box.E),
98 'boxF': new Form8949Line(Form8949Box.F),
99 };
100 };