From 872185653403854c516b00a26d0b9909ec23e8f1 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Mon, 13 Jul 2020 10:50:04 -0400 Subject: [PATCH] Add Form 6251. It could probably use a test, but the AMT is super annoying. --- src/fed2019/Form1099INT.ts | 2 +- src/fed2019/Form6251.ts | 208 +++++++++++++++++++++++++++++++++++++ src/fed2019/README.md | 1 + src/fed2019/Schedule2.ts | 21 +--- src/fed2019/index.ts | 1 + 5 files changed, 216 insertions(+), 17 deletions(-) create mode 100644 src/fed2019/Form6251.ts diff --git a/src/fed2019/Form1099INT.ts b/src/fed2019/Form1099INT.ts index adf4823..b038f99 100644 --- a/src/fed2019/Form1099INT.ts +++ b/src/fed2019/Form1099INT.ts @@ -44,7 +44,7 @@ export default class Form1099INT extends Form +// This program is free software licensed under the GNU General Public License, +// version 3.0. The full text of the license can be found in LICENSE.txt. +// SPDX-License-Identifier: GPL-3.0-only + +import { Form, TaxReturn } from '../core'; +import { AccumulatorLine, ComputedLine, ReferenceLine, UnsupportedLine, sumFormLines } from '../core/Line'; +import { clampToZero } from '../core/Math'; + +import Form1040, { QDCGTaxWorksheet, FilingStatus } from './Form1040'; +import Form1099INT from './Form1099INT'; +import Schedule1 from './Schedule1'; +import Schedule2 from './Schedule2'; +import Schedule3 from './Schedule3'; +import ScheduleD, { ScheduleDTaxWorksheet } from './ScheduleD'; + +export default class Form6251 extends Form { + readonly name = '6251'; + + readonly lines = { + // Part I + '1': new ComputedLine((tr): number => { + const f1040 = tr.getForm(Form1040); + const l11b = f1040.getValue(tr, '11b'); + if (l11b > 0) + return l11b; + return f1040.getValue(tr, '8b') - f1040.getValue(tr, '9') - f1040.getValue(tr, '10'); + }), + '2a': new ComputedLine((tr): number => { + // Not supported: Schedule A, line 7. + return tr.getForm(Form1040).getValue(tr, '9'); + }), + '2b': new ReferenceLine(Schedule1, '1', 'Tax refund', 0), // Not supported - line 8 SALT. + '2c': new UnsupportedLine('Investment interest expense'), + '2d': new UnsupportedLine('Depletion'), + '2e': new UnsupportedLine('Net operating loss deduction'), + '2f': new UnsupportedLine('Alternative tax net operating loss deduction'), + '2g': new AccumulatorLine(Form1099INT, '9', 'Interest from specified private activity bonds exempt from the regular tax'), + '2h': new UnsupportedLine('Qualified small business stock'), + '2i': new UnsupportedLine('Exercise of incentive stock options'), + '2j': new UnsupportedLine('Estates and trusts (amount from Schedule K-1 (Form 1041), box 12, code A)'), + '2k': new UnsupportedLine('Disposition of property'), + '2l': new UnsupportedLine('Depreciation on assets placed in service after 1986'), + '2m': new UnsupportedLine('Passive activities'), + '2n': new UnsupportedLine('Loss limitations'), + '2o': new UnsupportedLine('Circulation costs'), + '2p': new UnsupportedLine('Long-term contracts'), + '2q': new UnsupportedLine('Mining costs'), + '2r': new UnsupportedLine('Research and experimental costs'), + '2s': new UnsupportedLine('Income from certain installment sales before January 1, 1987'), + '2t': new UnsupportedLine('Intangible drilling costs preference'), + '3': new UnsupportedLine('Other adjustments'), + '4': new ComputedLine((tr): number => { + 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']); + }, 'Alternative minimum taxable income'), + + // Part II + '5': new ComputedLine((tr): number => { + // [ threshold, exemption ] + const exemptions = { + [FilingStatus.Single]: [ 510300, 71700 ], + [FilingStatus.MarriedFilingJoint]: [ 1020600, 111700 ], + [FilingStatus.MarriedFilingSeparate]: [ 510300, 55850 ], + }; + const exemption = exemptions[tr.getForm(Form1040).filingStatus]; + + const l4 = this.getValue(tr, '4'); + if (l4 < exemption[0]) + return exemption[1]; + + // Exemption worksheet: + const wl1 = exemption[1]; + const wl2 = l4; + const wl3 = exemption[0]; + const wl4 = clampToZero(wl2 - wl3); + const wl5 = wl4 * 0.25; + const wl6 = clampToZero(wl1 - wl5); + return wl6; + }), + '6': new ComputedLine((tr): number => { + return clampToZero(this.getValue(tr, '4') - this.getValue(tr, '5')); + }), + '7': new ComputedLine((tr): number => { + // Not supported - Form 2555. + // Not supported - Form1040 directly reporting cap gains on line 6. + + const f1040 = tr.getForm(Form1040); + + let part3 = f1040.getValue(tr, '3a') > 0; + + const schedD = tr.findForm(ScheduleD); + if (schedD) { + const flag = schedD.getValue(tr, '15') > 0 && schedD.getValue(tr, '16') > 0; + part3 = part3 || flag; + } + + if (part3) + return this.getValue(tr, '40'); + + return computeAmtTax(f1040.filingStatus, this.getValue(tr, '6')); + }), + '8': new ReferenceLine(Schedule3, '1', 'Alternative minimum tax foreign tax credit'), // Not supported - AMT FTC recalculation + '9': new ComputedLine((tr): number => { + return this.getValue(tr, '7') - this.getValue(tr, '8'); + }, 'Tentative minimum tax'), + '10': new ComputedLine((tr): number => { + let value = tr.getForm(Form1040).getValue(tr, '12a'); + const sched2 = tr.findForm(Schedule2); + if (sched2) { + value += sched2.getValue(tr, '2'); + } + // Not supported - subtracting Schedule3@1 for the AMT FTC. + return value; + }), + '11': new ComputedLine((tr): number => { + return clampToZero(this.getValue(tr, '9') - this.getValue(tr, '10')); + }, 'AMT'), + + // Part III + '12': new ReferenceLine(Form6251 as any, '6'), + '13': new ComputedLine((tr): number => { + const schedDTW = tr.findForm(ScheduleDTaxWorksheet); + if (schedDTW) + return schedDTW.getValue(tr, '13'); + + const qdcgtw = tr.getForm(QDCGTaxWorksheet); + return qdcgtw.getValue(tr, '6'); + }), + '14': new ReferenceLine(ScheduleD, '19', undefined, 0), + '15': new ComputedLine((tr): number => { + const value = this.getValue(tr, '13') + this.getValue(tr, '14'); + const schedDTW = tr.findForm(ScheduleDTaxWorksheet); + if (schedDTW) + return Math.min(value, schedDTW.getValue(tr, '10')); + + return value; + }), + '16': new ComputedLine((tr): number => Math.min(this.getValue(tr, '12'), this.getValue(tr, '15'))), + '17': new ComputedLine((tr): number => this.getValue(tr, '12') - this.getValue(tr, '16')), + '18': new ComputedLine((tr): number => { + const fs = tr.getForm(Form1040).filingStatus; + return computeAmtTax(fs, this.getValue(tr, '17')); + }), + '19': new ComputedLine((tr): number => { + switch (tr.getForm(Form1040).filingStatus) { + case FilingStatus.Single: + case FilingStatus.MarriedFilingSeparate: + return 39375; + case FilingStatus.MarriedFilingJoint: + return 78750; + } + }), + '20': new ComputedLine((tr): number => { + const schedDTW = tr.findForm(ScheduleDTaxWorksheet); + if (schedDTW) + return clampToZero(schedDTW.getValue(tr, '14')); + + const qdcgtw = tr.getForm(QDCGTaxWorksheet); + return clampToZero(qdcgtw.getValue(tr, '7')); + }), + '21': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '19') - this.getValue(tr, '20'))), + '22': new ComputedLine((tr): number => Math.min(this.getValue(tr, '12'), this.getValue(tr, '13'))), + '23': new ComputedLine((tr): number => Math.min(this.getValue(tr, '21'), this.getValue(tr, '22'))), + '24': new ComputedLine((tr): number => this.getValue(tr, '22') - this.getValue(tr, '23')), + '25': new ComputedLine((tr): number => { + switch (tr.getForm(Form1040).filingStatus) { + case FilingStatus.Single: return 434550; + case FilingStatus.MarriedFilingSeparate: return 244425; + case FilingStatus.MarriedFilingJoint: return 488850; + } + }), + '26': new ReferenceLine(Form6251 as any, '21'), + '27': new ComputedLine((tr): number => { + const schedDTW = tr.findForm(ScheduleDTaxWorksheet); + if (schedDTW) + return clampToZero(schedDTW.getValue(tr, '21')); + + const qdcgtw = tr.getForm(QDCGTaxWorksheet); + return clampToZero(qdcgtw.getValue(tr, '7')); + }), + '28': new ComputedLine((tr): number => this.getValue(tr, '26') + this.getValue(tr, '27')), + '29': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '25') - this.getValue(tr, '28'))), + '30': new ComputedLine((tr): number => Math.min(this.getValue(tr, '24'), this.getValue(tr, '29'))), + '31': new ComputedLine((tr): number => this.getValue(tr, '30') * 0.15), + '32': new ComputedLine((tr): number => this.getValue(tr, '23') + this.getValue(tr, '30')), + '33': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '22') - this.getValue(tr, '32'))), + '34': new ComputedLine((tr): number => this.getValue(tr, '33') * 0.20), + '35': new ComputedLine((tr): number => this.getValue(tr, '17') + this.getValue(tr, '32') + this.getValue(tr, '33')), + '36': new ComputedLine((tr): number => clampToZero(this.getValue(tr, '12') - this.getValue(tr, '35'))), + '37': new ComputedLine((tr): number => this.getValue(tr, '36') * 0.25), + '38': new ComputedLine((tr): number => sumFormLines(tr, this, ['18', '31', '34', '37'])), + '39': new ComputedLine((tr): number => { + const fs = tr.getForm(Form1040).filingStatus; + return computeAmtTax(fs, this.getValue(tr, '12')); + }), + '40': new ComputedLine((tr): number => Math.min(this.getValue(tr, '38'), this.getValue(tr, '39'))), + }; +}; + +function computeAmtTax(filingStatus, amount) { + const mfs = filingStatus = FilingStatus.MarriedFilingSeparate; + const limit = mfs ? 97400 : 194800; + const sub = mfs ? 1948 : 3896; + + if (amount < limit) + return amount * 0.26; + return (amount * 0.28) - sub; +} diff --git a/src/fed2019/README.md b/src/fed2019/README.md index d29ece4..b0a46ff 100644 --- a/src/fed2019/README.md +++ b/src/fed2019/README.md @@ -26,6 +26,7 @@ The following forms are at least partially supported: - **Form 1099-INT:** Interest income - **Form 1099-R:** Retirement account distributions - **Form 1116:** Foreign tax credit +- **Form 6251:** Alternative Minimum Tax _without Form 1116 and Schedule D recalculation_ - **Form 8606:** Nondeductible IRAs - **Form 8949:** Sales and dispositions of capital assets - **Form 8959:** Additional medicare tax diff --git a/src/fed2019/Schedule2.ts b/src/fed2019/Schedule2.ts index b2a2d51..87e2819 100644 --- a/src/fed2019/Schedule2.ts +++ b/src/fed2019/Schedule2.ts @@ -10,6 +10,7 @@ import { UnsupportedFeatureError } from '../core/Errors'; import Form1040, { FilingStatus } from './Form1040'; import Form1099DIV from './Form1099DIV'; import Form1099INT from './Form1099INT'; +import Form6251 from './Form6251'; import Form8959 from './Form8959'; import Form8960 from './Form8960'; @@ -18,22 +19,10 @@ export default class Schedule2 extends Form { readonly lines = { '1': new ComputedLine((tr): number => { - // TODO - this is just using Taxable Income, rather than AMT-limited - // income - const f1040 = tr.getForm(Form1040); - const taxableIncome = f1040.getValue(tr, '11b'); - switch (f1040.filingStatus) { - case FilingStatus.Single: - if (taxableIncome < 510300) - return 0; - case FilingStatus.MarriedFilingJoint: - if (taxableIncome < 1020600) - return 0; - case FilingStatus.MarriedFilingSeparate: - if (taxableIncome < 510300) - return 0; - } - throw new UnsupportedFeatureError('The AMT is not supported'); + const f6251 = tr.findForm(Form6251); + if (f6251) + return f6251.getValue(tr, '11'); + return 0; }, 'AMT'), '2': new UnsupportedLine('Excess advance premium tax credit repayment'), '3': new ComputedLine((tr): number => { diff --git a/src/fed2019/index.ts b/src/fed2019/index.ts index 1f9f7bb..152a588 100644 --- a/src/fed2019/index.ts +++ b/src/fed2019/index.ts @@ -9,6 +9,7 @@ export { default as Form1099DIV } from './Form1099DIV'; export { default as Form1099INT } from './Form1099INT'; export { default as Form1099R } from './Form1099R'; export { default as Form1116 } from './Form1116'; +export { default as Form6251 } from './Form6251'; export { default as Form8606 } from './Form8606'; export { default as Form8949 } from './Form8949'; export { default as Form8959 } from './Form8959'; -- 2.43.5