From 7f85b18b720c6ba38b9210d0fe0c4918491054f6 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Tue, 17 Mar 2020 01:47:52 -0400 Subject: [PATCH] Fix bugs in Schedule D and implement the QDCG Tax Worksheet. --- src/fed2019/Form1040.ts | 93 ++++++++++++++++++++++++++++++++--- src/fed2019/Schedule3.test.ts | 3 +- src/fed2019/ScheduleD.ts | 37 ++++++++------ 3 files changed, 108 insertions(+), 25 deletions(-) diff --git a/src/fed2019/Form1040.ts b/src/fed2019/Form1040.ts index eacae09..a4ace87 100644 --- a/src/fed2019/Form1040.ts +++ b/src/fed2019/Form1040.ts @@ -59,7 +59,7 @@ export default class Form1040 extends Form { return 0; const l6 = schedD.getValue(tr, '16'); - if (l6 > 0) + if (l6 >= 0) return l6; return schedD.getValue(tr, '21'); }, 'Capital gain/loss'), @@ -117,16 +117,24 @@ export default class Form1040 extends Form { // Not supported: // Form 8814 (election to report child's interest or dividends) // Form 4972 (relating to lump-sum distributions) - const taxableIncome = this.getValue(tr, '11b'); - if (this.getValue(tr, '3a') > 0 && !tr.findForm(ScheduleD)) - throw new UnsupportedFeatureError('Qualified Dividends and Captial Gains Tax Worksheet not supported, Schedule D requried'); + const schedD = tr.findForm(ScheduleD); + // Line 18 and 19 are not undefined or 0. + if (schedD && !schedD.getValue(tr, '20')) { + // Use ScheD tax worksheet; + const schedDtw = tr.findForm(ScheduleDTaxWorksheet); + if (schedDtw) + return schedDtw.getValue(tr, '47'); + } - const schedD = tr.findForm(ScheduleDTaxWorksheet); - if (schedD) - return schedD.getValue(tr, '47'); + // If there are qualified dividends, use the QDCGTW. + if (this.getValue(tr, '3a') > 0) { + const qdcgtw = tr.getForm(QDCGTaxWorksheet); + return qdcgtw.getValue(tr, '27'); + } - return computeTax(taxableIncome, this.filingStatus); + // Otherwise, compute just on taxable income. + return computeTax(this.getValue(tr, '11b'), this.filingStatus); }, 'Tax'), '12b': new ComputedLine((tr): number => { @@ -237,3 +245,72 @@ export function computeTax(income: number, filingStatus: FilingStatus): number { return ((income - bracketStart) * bracket[1]) + bracket[2]; }; + +export class QDCGTaxWorksheet extends Form { + readonly name = 'Qual Div Cap Gain Tax Worksheet'; + + protected readonly _lines = { + '1': new ReferenceLine(Form1040, '11b', 'Taxable income'), + '2': new ReferenceLine(Form1040, '3a', 'Qualified dividends'), + '3': new ComputedLine((tr): number => { + const schedD = tr.findForm(ScheduleD); + if (schedD) + return Math.min(schedD.getValue(tr, '15'), schedD.getValue(tr, '16')); + return tr.getForm(Form1040).getValue(tr, '6'); + }), + '4': new ComputedLine((tr): number => this.getValue(tr, '2') + this.getValue(tr, '3')), + '5': new ComputedLine(() => 0), // Not supported - Form 4952/4g (nvestment interest expense deduction) + '6': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '4') - this.getValue(tr, '5'))), + '7': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '1') - this.getValue(tr, '6'))), + '8': new ComputedLine((tr): number => { + switch (tr.getForm(Form1040).filingStatus) { + case FilingStatus.Single: + case FilingStatus.MarriedFilingSeparate: + return 39375; + case FilingStatus.MarriedFilingJoint: + return 78750; + }; + }), + '9': new ComputedLine((tr): number => Math.min(this.getValue(tr, '1'), this.getValue(tr, '8'))), + '10': new ComputedLine((tr): number => Math.min(this.getValue(tr, '7'), this.getValue(tr, '9'))), + '11': new ComputedLine((tr): number => { + return this.getValue(tr, '9') - this.getValue(tr, '10'); + }, 'Amount taxed at 0%'), + '12': new ComputedLine((tr): number => Math.min(this.getValue(tr, '1'), this.getValue(tr, '6'))), + '13': new ReferenceLine(QDCGTaxWorksheet as any, '11'), + '14': new ComputedLine((tr): number => this.getValue(tr, '12') - this.getValue(tr, '13')), + '15': new ComputedLine((tr): number => { + switch (tr.getForm(Form1040).filingStatus) { + case FilingStatus.Single: return 434550; + case FilingStatus.MarriedFilingSeparate: return 244425; + case FilingStatus.MarriedFilingJoint: return 488850; + }; + }), + '16': new ComputedLine((tr): number => Math.min(this.getValue(tr, '1'), this.getValue(tr, '15'))), + '17': new ComputedLine((tr): number => this.getValue(tr, '7') + this.getValue(tr, '11')), + '18': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '16') - this.getValue(tr, '17'))), + '19': new ComputedLine((tr): number => Math.min(this.getValue(tr, '14'), this.getValue(tr, '18'))), + '20': new ComputedLine((tr): number => { + return this.getValue(tr, '19') * 0.15; + }, 'Amount taxed at 15%'), + '21': new ComputedLine((tr): number => this.getValue(tr, '11') + this.getValue(tr, '19')), + '22': new ComputedLine((tr): number => this.getValue(tr, '12') - this.getValue(tr, '21')), + '23': new ComputedLine((tr): number => { + return this.getValue(tr, '22') * 0.20; + }, 'Amount taxed at 20%'), + '24': new ComputedLine((tr): number => { + return computeTax(this.getValue(tr, '7'), tr.getForm(Form1040).filingStatus); + }, 'Tax on line 7'), + '25': new ComputedLine((tr): number => { + return this.getValue(tr, '20') + + this.getValue(tr, '23') + + this.getValue(tr, '24'); + }), + '26': new ComputedLine((tr): number => { + return computeTax(this.getValue(tr, '1'), tr.getForm(Form1040).filingStatus); + }, 'Tax on line 1'), + '27': new ComputedLine((tr): number => { + return Math.min(this.getValue(tr, '25'), this.getValue(tr, '26')); + }, 'Tax on all taxable income') + }; +}; diff --git a/src/fed2019/Schedule3.test.ts b/src/fed2019/Schedule3.test.ts index 668ced6..c67915d 100644 --- a/src/fed2019/Schedule3.test.ts +++ b/src/fed2019/Schedule3.test.ts @@ -6,7 +6,7 @@ import { Person } from '../core'; import { NotFoundError } from '../core/Errors'; -import Form1040, { FilingStatus } from './Form1040'; +import Form1040, { QDCGTaxWorksheet, FilingStatus } from './Form1040'; import Form1099DIV from './Form1099DIV'; import Form1116 from './Form1116'; import Form8949 from './Form8949'; @@ -28,6 +28,7 @@ test('foreign tax credit, form 1116 not required', () => { tr.addForm(new Form1040({ filingStatus })); tr.addForm(new Form8949); tr.addForm(new ScheduleD); + tr.addForm(new QDCGTaxWorksheet); tr.addForm(new Form1099DIV({ payer: 'Brokerage', payee: p, diff --git a/src/fed2019/ScheduleD.ts b/src/fed2019/ScheduleD.ts index 94754e8..17fd11a 100644 --- a/src/fed2019/ScheduleD.ts +++ b/src/fed2019/ScheduleD.ts @@ -6,11 +6,11 @@ import { Form, Person, TaxReturn } from '../core'; import { Line, AccumulatorLine, ComputedLine, ReferenceLine, sumLineOfForms } from '../core/Line'; import { clampToZero } from '../core/Math'; -import { UnsupportedFeatureError } from '../core/Errors'; +import { NotFoundError, UnsupportedFeatureError } from '../core/Errors'; import Form8949, { Form8949Box } from './Form8949'; import Form1099DIV from './Form1099DIV'; -import Form1040, { FilingStatus, computeTax } from './Form1040'; +import Form1040, { FilingStatus, QDCGTaxWorksheet, computeTax } from './Form1040'; export default class ScheduleD extends Form { readonly name = 'Schedule D'; @@ -28,7 +28,7 @@ export default class ScheduleD extends Form { return f8949.getValue(tr, 'boxA').gainOrLoss + f8949.getValue(tr, 'boxB').gainOrLoss + f8949.getValue(tr, 'boxC').gainOrLoss; - }, 'Net short-term capital gain or (loss)'), + }, 'Net short-term capital gain or loss'), // 8a is not supported. @@ -46,40 +46,46 @@ export default class ScheduleD extends Form { f8949.getValue(tr, 'boxE').gainOrLoss + f8949.getValue(tr, 'boxF').gainOrLoss + this.getValue(tr, '13'); - }, 'Net long-term capital gain or (loss)'), + }, 'Net long-term capital gain or loss'), '16': new ComputedLine((tr): number => { return this.getValue(tr, '7') + this.getValue(tr, '15'); - }), + // If value is a gain, enter on 1040/6 and goto 17. + // If value is a loss, goto 21 and 22. + // If value is zero, enter 0 on 1040/6 and goto 22. + }, 'Total capital gain or loss'), '17': new ComputedLine((tr): boolean => { return this.getValue(tr, '15') > 0 && this.getValue(tr, '16') > 0; + // If yes, goto 18. + // If no, goto 22. }, 'Both ST and LT are gains'), '18': new ComputedLine((tr): number | undefined => { - if (!this.getValue(tr, '17') || this.getValue(tr, '16') <= 0) - return undefined; // Not supported - only for gains on Qualified Small Business Stock or collectibles. return 0; }, '28% Rate Gain Worksheet Value'), - // 19 is not supported (Unrecaptured Section 1250 Gain Worksheet) + '19': new ComputedLine(() => undefined, 'Unrecaptured Section 1250 Gain Worksheet'), // Not supported. '20': new ComputedLine((tr): boolean | undefined => { - if (!this.getValue(tr, '17') || this.getValue(tr, '16') <= 0) - return undefined; const l18 = this.getValue(tr, '18'); - const l19 = undefined; //this.getValue(tr, '19'); + const l19 = 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): number | undefined => { - if (!this.getValue(tr, '17') || !this.getValue(tr, '20')) - return undefined; + const l16 = this.getValue(tr, '16'); + if (l16 >= 0) + return 0; const filingStatus = tr.getForm(Form1040).filingStatus; const limit = filingStatus == FilingStatus.MarriedFilingSeparate ? -1500 : -3000; - return Math.min(this.getValue(tr, '16'), limit); + return Math.max(l16, limit); }, 'Net capital loss'), + + '22': new ComputedLine((tr): boolean => { + return tr.getForm(Form1040).getValue(tr, '3a') > 0; + }, 'Need QD/CG Tax Worksheet'), }; }; @@ -105,8 +111,7 @@ export class ScheduleDTaxWorksheet extends Form '10': new ComputedLine((tr): number => this.getValue(tr, '6') + this.getValue(tr, '9')), '11': new ComputedLine((tr): number => { const schedD = tr.getForm(ScheduleD); - // TODO - line 19 is not supported. - return Math.min(schedD.getValue(tr, '18'), Infinity); //schedD.getValue(tr, '19')); + return schedD.getValue(tr, '18') + schedD.getValue(tr, '19'); }), '12': new ComputedLine((tr): number => Math.min(this.getValue(tr, '9'), this.getValue(tr, '11'))), '13': new ComputedLine((tr): number => this.getValue(tr, '10') - this.getValue(tr, '12')), -- 2.22.5