From 9cc514be07b8e203ca19e3faaf1cd0eb815c2d58 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Sun, 23 Feb 2020 00:02:26 -0500 Subject: [PATCH] Initial work on Schedule D, Form 8949, and 1099-B. --- src/Line.ts | 9 +- src/fed2019/Form1040.test.ts | 21 +++++ src/fed2019/Form1040.ts | 12 ++- src/fed2019/Form1099B.ts | 76 ++++++++++++++++ src/fed2019/Form8949.test.ts | 159 +++++++++++++++++++++++++++++++++ src/fed2019/Form8949.ts | 167 +++++++++++++++++++++++++++++++++++ src/fed2019/ScheduleD.ts | 131 +++++++++++++++++++++++++++ 7 files changed, 572 insertions(+), 3 deletions(-) create mode 100644 src/fed2019/Form1099B.ts create mode 100644 src/fed2019/Form8949.test.ts create mode 100644 src/fed2019/Form8949.ts create mode 100644 src/fed2019/ScheduleD.ts diff --git a/src/Line.ts b/src/Line.ts index e0e974a..8fec28a 100644 --- a/src/Line.ts +++ b/src/Line.ts @@ -93,7 +93,12 @@ export class AccumulatorLine, value(tr: TaxReturn): number { const forms: F[] = tr.findForms(this._form); - const reducer = (acc: number, curr: F) => acc + curr.getValue(tr, this._line); - return forms.reduce(reducer, 0); + return sumLineOfForms(tr, forms, this._line); } }; + +export function sumLineOfForms, L extends keyof F['lines']>( + tr: TaxReturn, forms: F[], line: L): number { + const reducer = (acc: number, curr: F) => acc + curr.getValue(tr, line); + return forms.reduce(reducer, 0); +} diff --git a/src/fed2019/Form1040.test.ts b/src/fed2019/Form1040.test.ts index 795ddd0..7d8b77b 100644 --- a/src/fed2019/Form1040.test.ts +++ b/src/fed2019/Form1040.test.ts @@ -4,7 +4,10 @@ import TaxReturn from '../TaxReturn'; import Form1040, { FilingStatus, Schedule2 } from './Form1040'; import Form1099DIV from './Form1099DIV'; import Form1099INT from './Form1099INT'; +import Form1099B, { GainType } from './Form1099B'; +import ScheduleD from './ScheduleD'; import Form8959 from './Form8959'; +import Form8949 from './Form8949'; import FormW2 from './FormW2'; test('w2 wages', () => { @@ -75,3 +78,21 @@ test('dividend income', () => { expect(f1040.getValue(tr, '3a')).toBe(75 * 2); expect(f1040.getValue(tr, '3b')).toBe(200); }); + +test('capital gain/loss', () => { + const p = Person.self('A'); + const tr = new TaxReturn(2019); + tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); + tr.addForm(new Form1099B({ + payer: 'Brokerage', + payee: p, + description: '10 FNDC', + proceeds: 1000, + costBasis: 800, + gainType: GainType.LongTerm, + basisReportedToIRS: true + })); + Form8949.addForms(tr, []); + tr.addForm(new ScheduleD()); + tr.getForm(ScheduleD).getValue(tr, '21'); +}); diff --git a/src/fed2019/Form1040.ts b/src/fed2019/Form1040.ts index f1106d8..273b3b8 100644 --- a/src/fed2019/Form1040.ts +++ b/src/fed2019/Form1040.ts @@ -7,6 +7,7 @@ import Form8959 from './Form8959'; import Form1099INT from './Form1099INT'; import Form1099DIV from './Form1099DIV'; import FormW2 from './FormW2'; +import ScheduleD from './ScheduleD'; export enum FilingStatus { Single, @@ -34,7 +35,16 @@ export default class Form1040 extends Form { '4d': new ComputedLine(() => 0), // 4c and 4d are not supported // 5a and 5b are not supported - '6': new ReferenceLine(/*'Schedule D'*/ undefined, '21', 'Capital gain/loss', 0), + '6': new ComputedLine((tr: TaxReturn): number => { + const schedD = tr.findForm(ScheduleD); + if (!schedD) + return 0; + + const l6 = schedD.getValue(tr, '16'); + if (l6 > 0) + return l6; + return schedD.getValue(tr, '21'); + }, 'Capital gain/loss'), '7a': new ReferenceLine(/*'Schedule 1'*/ undefined, '9', 'Other income from Schedule 1', 0), '7b': new ComputedLine((tr: TaxReturn): number => { diff --git a/src/fed2019/Form1099B.ts b/src/fed2019/Form1099B.ts new file mode 100644 index 0000000..7eb1af8 --- /dev/null +++ b/src/fed2019/Form1099B.ts @@ -0,0 +1,76 @@ +import Form from '../Form'; +import Person from '../Person'; +import TaxReturn from '../TaxReturn'; +import { InputLine } from '../Line'; + +export enum GainType { + ShortTerm = 'ST', + LongTerm = 'LT', + Ordinary = 'O', +}; + +export interface SpecialProceeds { + collectibles?: boolean; + qof?: boolean; +}; + +export interface IRSReporting { + grossProceeds?: boolean; + netProceeds?: boolean; +}; + +export interface Form1099BInput { + payer: string; + payee: Person; + description: string; + dateAcquired?: string; + dateSold?: string; + proceeds: number; + costBasis: number; + accruedMarketDiscount?: number; + washSaleLossDisallowed?: number; + gainType: GainType; + specialProceeds?: SpecialProceeds; + fedIncomeTax?: number; + nonCoveredSecurity?: boolean; + irsReporting?: IRSReporting; + disallowedLoss?: boolean; + profitOnClosedContracts?: number; + unrealizedProfitOnOpenContractsCurrentTY?: number; + unrealizedProfitOnOpenContractsNextTY?: number; + aggregateProfitOnContracts?: number; + basisReportedToIRS?: boolean; + bartering?: number; +}; + +class Input extends InputLine {}; + +export default class Form1099B extends Form { + readonly name = '1099-B'; + + readonly supportsMultipleCopies = true; + + protected readonly _lines = { + 'payer': new Input('payer'), + 'recipient': new Input('payee'), + '1a': new Input('description'), + '1b': new Input('dateAcquired'), + '1c': new Input('dateSold'), + '1d': new Input('proceeds'), + '1e': new Input('costBasis'), + '1f': new Input('accruedMarketDiscount'), + '1g': new Input('washSaleLossDisallowed'), + '2': new Input('gainType'), + '3': new Input('specialProceeds'), + '4': new Input('fedIncomeTax'), + '5': new Input('nonCoveredSecurity'), + '6': new Input('irsReporting'), + '7': new Input('disallowedLoss'), + '8': new Input('profitOnClosedContracts'), + '9': new Input('unrealizedProfitOnOpenContractsCurrentTY'), + '10': new Input('unrealizedProfitOnOpenContractsNextTY'), + '11': new Input('aggregateProfitOnContracts'), + '12': new Input('basisReportedToIRS'), + '13': new Input('bartering') + }; +}; diff --git a/src/fed2019/Form8949.test.ts b/src/fed2019/Form8949.test.ts new file mode 100644 index 0000000..0ec6323 --- /dev/null +++ b/src/fed2019/Form8949.test.ts @@ -0,0 +1,159 @@ +import TaxReturn from '../TaxReturn'; +import Person from '../Person'; + +import Form1040, { FilingStatus } from './Form1040'; +import Form1099B, { GainType } from './Form1099B'; +import Form8949, { Form8949Box } from './Form8949'; + +describe('single form', () => { + for (const box of [Form8949Box.A, Form8949Box.B, Form8949Box.D, Form8949Box.E]) { + test(`box ${Form8949Box[box]}`, () => { + const p = Person.self('A'); + const tr = new TaxReturn(2019); + tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); + tr.addForm(new Form1099B({ + payer: 'Brokerage', + payee: p, + description: '10 shares', + proceeds: 100, + costBasis: 110, + gainType: (box == Form8949Box.A || box == Form8949Box.B) ? GainType.ShortTerm : GainType.LongTerm, + basisReportedToIRS: (box == Form8949Box.A || box == Form8949Box.D), + })); + Form8949.addForms(tr, []); + + const f8949s = tr.findForms(Form8949); + expect(f8949s.length).toBe(6); + + for (let form of f8949s) { + if (form.getValue(tr, 'Box') == box) { + expect(form.getValue(tr, '2(d)')).toBe(100); + expect(form.getValue(tr, '2(e)')).toBe(110); + expect(form.getValue(tr, '2(g)')).toBe(0); + } else { + expect(form.getValue(tr, '2(d)')).toBe(0); + expect(form.getValue(tr, '2(e)')).toBe(0); + expect(form.getValue(tr, '2(g)')).toBe(0); + } + } + }); + } +}); + +test('multiple forms', () => { + const p = Person.self('A'); + const tr = new TaxReturn(2019); + tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); + tr.addForm(new Form1099B({ + payer: 'Brokerage', + payee: p, + description: '10 SCHB', + proceeds: 55, + costBasis: 50, + gainType: GainType.ShortTerm, + basisReportedToIRS: true, + })); + tr.addForm(new Form1099B({ + payer: 'Brokerage', + payee: p, + description: '10 SCHB', + proceeds: 55, + costBasis: 50, + gainType: GainType.LongTerm, + basisReportedToIRS: false, + })); + tr.addForm(new Form1099B({ + payer: 'Brokerage', + payee: p, + description: '10 SCHF', + proceeds: 22.40, + costBasis: 10.10, + gainType: GainType.LongTerm, + basisReportedToIRS: false, + })); + Form8949.addForms(tr, []); + + const f8949s = tr.findForms(Form8949); + expect(f8949s.length).toBe(6); + + const boxA = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.A).pop(); + expect(boxA.getValue(tr, '2(d)')).toBe(55); + expect(boxA.getValue(tr, '2(e)')).toBe(50); + expect(boxA.getValue(tr, '2(g)')).toBe(0); + + const boxE = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.E).pop(); + expect(boxE.getValue(tr, '2(d)')).toBe(77.40); + expect(boxE.getValue(tr, '2(e)')).toBe(60.10); + expect(boxE.getValue(tr, '2(g)')).toBe(0); + + const otherBoxes = f8949s.filter(f => ![Form8949Box.A, Form8949Box.E].includes(f.getValue(tr, 'Box'))); + for (const other of otherBoxes) { + expect(other.getValue(tr, '2(d)')).toBe(0); + expect(other.getValue(tr, '2(e)')).toBe(0); + expect(other.getValue(tr, '2(g)')).toBe(0); + } +}); + +test('adjustments', () => { + const p = Person.self('A'); + const tr = new TaxReturn(2019); + tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); + const b1 = new Form1099B({ + payer: 'Brokerage', + payee: p, + description: '10 SCHB', + proceeds: 55, + costBasis: 50, + gainType: GainType.ShortTerm, + basisReportedToIRS: false, + }); + tr.addForm(b1); + const b2 = new Form1099B({ + payer: 'Brokerage', + payee: p, + description: '10 SCHB', + proceeds: 18, + costBasis: 25, + gainType: GainType.LongTerm, + basisReportedToIRS: false, + }); + tr.addForm(b2); + tr.addForm(new Form1099B({ + payer: 'Brokerage', + payee: p, + description: '10 SCHF', + proceeds: 22.40, + costBasis: 10.10, + gainType: GainType.LongTerm, + basisReportedToIRS: true, + })); + Form8949.addForms(tr, [ + { entry: b1, code: 'W', amount: -10 }, + { entry: b2, code: 'W', amount: 90 }, + ]); + + const f8949s = tr.findForms(Form8949); + expect(f8949s.length).toBe(6); + + const boxA = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.B).pop(); + expect(boxA.getValue(tr, '2(d)')).toBe(55); + expect(boxA.getValue(tr, '2(e)')).toBe(50); + expect(boxA.getValue(tr, '2(g)')).toBe(-10); + + const boxD = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.D).pop(); + expect(boxD.getValue(tr, '2(d)')).toBe(22.40); + expect(boxD.getValue(tr, '2(e)')).toBe(10.10); + expect(boxD.getValue(tr, '2(g)')).toBe(0); + + const boxE = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.E).pop(); + expect(boxE.getValue(tr, '2(d)')).toBe(18); + expect(boxE.getValue(tr, '2(e)')).toBe(25); + expect(boxE.getValue(tr, '2(g)')).toBe(90); + + const otherBoxes = f8949s.filter(f => ![Form8949Box.B, Form8949Box.D, Form8949Box.E].includes(f.getValue(tr, 'Box'))); + for (const other of otherBoxes) { + expect(other.getValue(tr, '2(d)')).toBe(0); + expect(other.getValue(tr, '2(e)')).toBe(0); + expect(other.getValue(tr, '2(g)')).toBe(0); + } +}); diff --git a/src/fed2019/Form8949.ts b/src/fed2019/Form8949.ts new file mode 100644 index 0000000..9cd9ed9 --- /dev/null +++ b/src/fed2019/Form8949.ts @@ -0,0 +1,167 @@ +import Form from '../Form'; +import Person from '../Person'; +import TaxReturn from '../TaxReturn'; +import { Line, InputLine, ComputedLine, sumLineOfForms } from '../Line'; + +import Form1099B, { GainType } from './Form1099B'; + +export enum Form8949Box { + A = 'A', // Short-term transactions reported on Form(s) 1099-B showing basis was reported to the IRS + B = 'B', // Short-term transactions reported on Form(s) 1099-B showing basis wasn’t reported to the IRS + C = 'C', // Short-term transactions not reported to you on Form 1099-B + D = 'D', // Long-term transactions reported on Form(s) 1099-B showing basis was reported to the IRS + E = 'E', // Long-term transactions reported on Form(s) 1099-B showing basis wasn’t reported to the IRS + F = 'F', // Long-term transactions not reported to you on Form 1099-B +}; + +export interface Adjustment { + entry: Form1099B; + code: string; + amount: number; +}; + +export interface Form8949Input { + box: Form8949Box; + adjustments?: Adjustment[]; +}; + +function matching1099Bs(tr: TaxReturn, box: Form8949Box): Form1099B[] { + return tr.findForms(Form1099B).filter(f => { + const gainType: GainType = f.getValue(tr, '2'); + const basisReported: boolean = f.getValue(tr, '12'); + + switch (box) { + case Form8949Box.A: + return gainType == GainType.ShortTerm && basisReported; + case Form8949Box.B: + return gainType == GainType.ShortTerm && !basisReported; + case Form8949Box.D: + return gainType == GainType.LongTerm && basisReported; + case Form8949Box.E: + return gainType == GainType.LongTerm && !basisReported; + }; + + return false; + }); +} + +class Form8949Line extends Line { + private _box: Form8949Box; + private _line: keyof Form1099B['lines']; + + constructor(f8949Functor: () => Form8949, line: keyof Form1099B['lines'], description: string) { + const box = f8949Functor().getInput('box'); + super(`Form 8949 Box ${box} total of 1099-B ${line} (${description})`); + this._box = box; + this._line = line; + } + + value(tr: TaxReturn): number { + const f1099bs = matching1099Bs(tr, this._box); + return sumLineOfForms(tr, f1099bs, this._line); + } +}; + +export default class Form8949 extends Form { + readonly name = '8949'; + + readonly supportsMultipleCopies = true; + + protected readonly _lines = { + 'Box': new InputLine('box'), + '2(d)': new Form8949Line(() => this, '1d', 'proceeds'), + '2(e)': new Form8949Line(() => this, '1e', 'cost'), + '2(g)': new ComputedLine((tr: TaxReturn): number => { + const f1099bs = matching1099Bs(tr, this.getInput('box')); + const adjustments = this.getInput('adjustments').filter((a: Adjustment): boolean => { + return f1099bs.includes(a.entry); + }); + return adjustments.reduce((acc, curr) => acc + curr.amount, 0); + }, 'adjustments') + }; + + static addForms(tr: TaxReturn, adjustments: Adjustment[]) { + tr.addForm(new Form8949({ box: Form8949Box.A, adjustments })); + tr.addForm(new Form8949({ box: Form8949Box.B, adjustments })); + tr.addForm(new Form8949({ box: Form8949Box.C, adjustments })); + tr.addForm(new Form8949({ box: Form8949Box.D, adjustments })); + tr.addForm(new Form8949({ box: Form8949Box.E, adjustments })); + tr.addForm(new Form8949({ box: Form8949Box.F, adjustments })); + } +}; + +/* +export interface Adjustment { + entry: Form1099B; + code: string; + amount: number; +}; + +export interface Form8949Input { + adjustments?: []Adjustment; +}; + +export interface Form8949Total { + proceeds: number; + costBasis: number; + adjustmentAmount: number; + gainOrLoss: number; +}; + +class Form8949Line extends Line { + private _box: Form8949Box; + + constructor(description: string, box: Form8949Input) { + super(description); + this._box = box; + } + + value(tr: TaxReturn): Form8949Total { + const lineShortTerm = this._box == Form8949Box.A || this._box == Form8949Box.B || this._box == Form8949Box.C; + const lineBasisReported = this._box == Form8949Box.A || this._box == Form8949Box.D; + + const f1099bs = tr.findForms(Form1099B); + const relevant1099bs: Form1099B[] = []; + for (const form of f1099bs) { + const gainType = form.getValue(tr, '2'); + const basisReported = form.getValue(tr, '12'); + + if (lineBasisReported != basisReported) + continue; + + if (gainType == GainType.ShortTerm && lineShortTerm) { + relevant1099bs.push(form); + } else if (gainType == GainType.LongTerm && !lineShortTerm) { + relevant1099bs.push(form); + } + } + + const sumValues = (line: keyof Form1099B['lines']) => + relevant1099bs.map((f: Form1099B): number => f.getValue(tr, line)) + .reduce((acc, curr) => acc + curr, 0); + + const proceeds = sumValues('1d'); + const costBasis = sumValues('1e'); + + return { + proceeds, + costBasis, + adjustmentAmount: 0, + gainOrLoss: costBasis - proceeds, + }; + } +}; + +export default class Form8949 extends Form { + readonly name = '8949'; + + protected readonly _lines = { + boxATotals: new Form8949Line('Short-term basis reported', Form8949Box.A), + boxBTotals: new Form8949Line('Short-term basis NOT reported', Form8949Box.B), + boxCTotals: new Form8949Line('Short-term unreported', Form8949Box.C), + boxDTotals: new Form8949Line('Long-term basis reported', Form8949Box.D), + boxETotals: new Form8949Line('Long-term basis NOT reported', Form8949Box.E), + boxFTotals: new Form8949Line('Long-term unreported', Form8949Box.F) + }; +}; +*/ diff --git a/src/fed2019/ScheduleD.ts b/src/fed2019/ScheduleD.ts new file mode 100644 index 0000000..ccf1e34 --- /dev/null +++ b/src/fed2019/ScheduleD.ts @@ -0,0 +1,131 @@ +import Form from '../Form'; +import Person from '../Person'; +import TaxReturn from '../TaxReturn'; +import { Line, AccumulatorLine, ComputedLine, sumLineOfForms } from '../Line'; + +import Form8949, { Form8949Box } from './Form8949'; +import Form1099DIV from './Form1099DIV'; +import Form1040, { FilingStatus } from './Form1040'; + +class ScheduleDTotal extends Line { + private _line: keyof Form8949['lines']; + private _box: Form8949Box; + + constructor(description: string, line: keyof Form8949['lines'], box: Form8949Box) { + super(description); + this._line = line; + this._box = box; + } + + value(tr: TaxReturn): number { + const forms: Form8949[] = tr.findForms(Form8949).filter(f => f.getValue(tr, 'Box') == this._box); + return sumLineOfForms(tr, forms, this._line); + } +}; + +export default class ScheduleD extends Form { + readonly name = 'Schedule D'; + + protected readonly _lines = { + // 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) + + '1b(d)': new ScheduleDTotal('Proceeds of short-term basis reported', '2(d)', Form8949Box.A), + '1b(e)': new ScheduleDTotal('Cost basis of short-term basis-reported', '2(e)', Form8949Box.A), + '1b(g)': new ScheduleDTotal('Adjustments to short-term basis-reported', '2(g)', Form8949Box.A), + '1b(h)': new ComputedLine((tr: TaxReturn): number => { + return (this.getValue(tr, '1b(d)') - this.getValue(tr, '1b(e)')) + this.getValue(tr, '1b(g)'); + }, 'Gain of short-term basis reported'), + + '2(d)': new ScheduleDTotal('Proceeds of short-term basis NOT reported', '2(d)', Form8949Box.B), + '2(e)': new ScheduleDTotal('Cost basis of short-term NOT basis-reported', '2(e)', Form8949Box.B), + '2(g)': new ScheduleDTotal('Adjustments to short-term NOT basis-reported', '2(g)', Form8949Box.B), + '2(h)': new ComputedLine((tr: TaxReturn): number => { + return (this.getValue(tr, '2(d)') - this.getValue(tr, '2(e)')) + this.getValue(tr, '2(g)'); + }, 'Gain of short-term basis NOT reported'), + + '3(d)': new ScheduleDTotal('Proceeds of short-term basis unreported', '2(d)', Form8949Box.C), + '3(e)': new ScheduleDTotal('Cost basis of short-term unreported', '2(e)', Form8949Box.C), + '3(g)': new ScheduleDTotal('Adjustments to short-term unreported', '2(g)', Form8949Box.C), + '3(h)': new ComputedLine((tr: TaxReturn): number => { + return (this.getValue(tr, '3(d)') - this.getValue(tr, '3(e)')) + this.getValue(tr, '3(g)'); + }, 'Gain of short-term unreported'), + + // 4 is not supported (Short-term gain from Form 6252 and short-term gain or (loss) from Forms 4684, 6781, and 8824) + // 5 is not supported (Net short-term gain or (loss) from partnerships, S corporations, estates, and trusts from Schedule(s) K-1) + // 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) + + '7': new ComputedLine((tr: TaxReturn): number => { + // 4-6 should be included. + return this.getValue(tr, '1b(h)') + this.getValue(tr, '2(h)'), this.getValue(tr, '3(h)'); + }, 'Net short-term capital gain or (loss)'), + + // 8a is not supported. + + '8b(d)': new ScheduleDTotal('Proceeds of long-term basis reported', '2(d)', Form8949Box.D), + '8b(e)': new ScheduleDTotal('Cost basis of long-term basis-reported', '2(e)', Form8949Box.D), + '8b(g)': new ScheduleDTotal('Adjustments to long-term basis-reported', '2(g)', Form8949Box.D), + '8b(h)': new ComputedLine((tr: TaxReturn): number => { + return (this.getValue(tr, '8b(d)') - this.getValue(tr, '8b(e)')) + this.getValue(tr, '8b(g)'); + }, 'Gain of long-term basis reported'), + + '9(d)': new ScheduleDTotal('Proceeds of long-term basis NOT reported', '2(d)', Form8949Box.E), + '9(e)': new ScheduleDTotal('Cost basis of long-term NOT basis-reported', '2(e)', Form8949Box.E), + '9(g)': new ScheduleDTotal('Adjustments to long-term NOT basis-reported', '2(g)', Form8949Box.E), + '9(h)': new ComputedLine((tr: TaxReturn): number => { + return (this.getValue(tr, '9(d)') - this.getValue(tr, '9(e)')) + this.getValue(tr, '9(g)'); + }, 'Gain of long-term basis NOT reported'), + + '10(d)': new ScheduleDTotal('Proceeds of long-term basis unreported', '2(d)', Form8949Box.F), + '10(e)': new ScheduleDTotal('Cost basis of long-term unreported', '2(e)', Form8949Box.F), + '10(g)': new ScheduleDTotal('Adjustments to long-term unreported', '2(g)', Form8949Box.F), + '10(h)': new ComputedLine((tr: TaxReturn): number => { + return (this.getValue(tr, '10(d)') - this.getValue(tr, '10(e)')) + this.getValue(tr, '10(g)'); + }, 'Gain of long-term unreported'), + + // 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) + // 12 is not supported (Net long-term gain or (loss) from partnerships, S corporations, estates, and trusts from Schedule(s) K-1) + + '13': new AccumulatorLine(Form1099DIV, '2a', 'Capital gain distributions'), + + // 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) + + '15': new ComputedLine((tr: TaxReturn): number => { + // 11-14 should be included. + return this.getValue(tr, '8b(h)') + this.getValue(tr, '9(h)') + this.getValue(tr, '10(h)') + + this.getValue(tr, '13'); + }, 'Net long-term capital gain or (loss)'), + + '16': new ComputedLine((tr: TaxReturn): number => { + return this.getValue(tr, '7') + this.getValue(tr, '15'); + }), + + '17': new ComputedLine((tr: TaxReturn): boolean => { + return this.getValue(tr, '15') > 0 && this.getValue(tr, '16') > 0; + }, 'Both ST and LT are gains'), + + '18': new ComputedLine((tr: TaxReturn): number | undefined => { + if (!this.getValue(tr, '17')) + return undefined; + // TODO + return 0; + }, '28% Rate Gain Worksheet Value'), + + // 19 is not supported (Unrecaptured Section 1250 Gain Worksheet) + + '20': new ComputedLine((tr: TaxReturn): boolean | undefined => { + if (!this.getValue(tr, '17')) + return undefined; + const l18 = this.getValue(tr, '18'); + const l19 = undefined; //this.getValue(tr, '19'); + return (l18 === 0 || l18 === undefined) || (l19 === 0 || l19 === undefined); + }, 'Line 18 and 19 both 0 or blank?'), + + '21': new ComputedLine((tr: TaxReturn): number | undefined => { + if (!this.getValue(tr, '17')) + return undefined; + const filingStatus = tr.getForm(Form1040).getInput('filingStatus'); + const limit = filingStatus == FilingStatus.MarriedFilingSeparate ? -1500 : -3000; + return Math.min(this.getValue(tr, '16'), limit); + }, 'Net capital loss'), + }; +}; -- 2.22.5