From eb3048bd931f950598487ae2362994ec914f9f31 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Sun, 22 Mar 2020 17:46:37 -0400 Subject: [PATCH] Redo 1099-B to be simplerf for recording adjustments. This no longer puts adjustments ont Form8949, since doing so required keeping a reference to the Form1099B. Instead, organize Form1099B as most brokerages report it, as a composite statement. --- src/fed2019/Form1040.test.ts | 14 ++-- src/fed2019/Form1099B.ts | 73 +++++++-------------- src/fed2019/Form1116.test.ts | 30 ++++----- src/fed2019/Form8949.test.ts | 121 ++++++++++++++++++----------------- src/fed2019/Form8949.ts | 84 +++++++++++------------- src/fed2019/Form8960.test.ts | 26 ++++---- src/fed2019/Form8995.test.ts | 14 ++-- 7 files changed, 170 insertions(+), 192 deletions(-) diff --git a/src/fed2019/Form1040.test.ts b/src/fed2019/Form1040.test.ts index 7ae22de..109b74f 100644 --- a/src/fed2019/Form1040.test.ts +++ b/src/fed2019/Form1040.test.ts @@ -9,7 +9,7 @@ import { NotFoundError } from '../core/Errors'; import Form1040, { FilingStatus } from './Form1040'; import Form1099DIV from './Form1099DIV'; import Form1099INT from './Form1099INT'; -import Form1099B, { GainType } from './Form1099B'; +import Form1099B from './Form1099B'; import Form1099R, { Box7Code } from './Form1099R'; import Schedule2 from './Schedule2'; import ScheduleD, { ScheduleDTaxWorksheet } from './ScheduleD'; @@ -127,11 +127,13 @@ test('capital gain/loss', () => { tr.addForm(new Form1099B({ payer: 'Brokerage', payee: p, - description: '10 FNDC', - proceeds: 1000, - costBasis: 800, - gainType: GainType.LongTerm, - basisReportedToIRS: true + longTermBasisReported: [ + { + description: '10 FNDC', + proceeds: 1000, + costBasis: 800, + } + ] })); tr.addForm(new Form8949); tr.addForm(new ScheduleD()); diff --git a/src/fed2019/Form1099B.ts b/src/fed2019/Form1099B.ts index 3cf59ba..816cfef 100644 --- a/src/fed2019/Form1099B.ts +++ b/src/fed2019/Form1099B.ts @@ -6,25 +6,7 @@ import { Form, Person, TaxReturn } from '../core'; import { InputLine } from '../core/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; +export interface Form1099BRow { description: string; dateAcquired?: string; dateSold?: string; @@ -32,18 +14,24 @@ export interface Form1099BInput { 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; + + adjustments?: number; // Not reported on 1099-B, but entered as part of Form8994. +}; + +export interface Form1099BInput { + payer: string; + payee: Person; + + shortTermBasisReported?: Form1099BRow[]; // Box A checked. + shortTermBasisUnreported?: Form1099BRow[]; // Box B checked. + + longTermBasisReported?: Form1099BRow[]; // Box D checked. + longTermBasisUnreported?: Form1099BRow[]; // Box E checked. + + // For unreported securities, create your own 1099-B. + shortTermUnreported?: Form1099BRow[]; // Box C checked. + longTermUnreported?: Form1099BRow[]; // Box F checked. }; class Input extends InputLine {}; @@ -58,24 +46,11 @@ export default class Form1099B extends Form 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') + 'shortTermBasisReported': new Input('shortTermBasisReported'), + 'shortTermBasisUnreported': new Input('shortTermBasisUnreported'), + 'longTermBasisReported': new Input('longTermBasisReported'), + 'longTermBasisUnreported': new Input('longTermBasisUnreported'), + 'shortTermUnreported': new Input('shortTermUnreported'), + 'longTermUnreported': new Input('longTermUnreported'), }; }; diff --git a/src/fed2019/Form1116.test.ts b/src/fed2019/Form1116.test.ts index 77089ec..cc6eedf 100644 --- a/src/fed2019/Form1116.test.ts +++ b/src/fed2019/Form1116.test.ts @@ -8,7 +8,7 @@ import { UnsupportedFeatureError } from '../core/Errors'; import Form1040, { FilingStatus } from './Form1040'; import Form1116, { ForeignIncomeCategory } from './Form1116'; -import Form1099B, { GainType } from './Form1099B'; +import Form1099B from './Form1099B'; import Form1099DIV from './Form1099DIV'; import Form8949 from './Form8949'; import W2 from './W2'; @@ -104,20 +104,20 @@ test('no net capital losses in total income', () => { tr.addForm(new Form1099B({ payer: 'Brokerage', payee: p, - description: 'SCHF', - proceeds: 100, - costBasis: 50, - gainType: GainType.LongTerm, - basisReportedToIRS: true - })); - tr.addForm(new Form1099B({ - payer: 'Brokerage', - payee: p, - description: 'SCHE', - proceeds: 60, - costBasis: 100, - gainType: GainType.ShortTerm, - basisReportedToIRS: true + longTermBasisReported: [ + { + description: 'SCHF', + proceeds: 100, + costBasis: 50, + } + ], + shortTermBasisReported: [ + { + description: 'SCHE', + proceeds: 60, + costBasis: 100, + } + ] })); tr.addForm(new Form8949); tr.addForm(new ScheduleD); diff --git a/src/fed2019/Form8949.test.ts b/src/fed2019/Form8949.test.ts index 2578366..90d2ad3 100644 --- a/src/fed2019/Form8949.test.ts +++ b/src/fed2019/Form8949.test.ts @@ -6,7 +6,7 @@ import { Person } from '../core'; import Form1040, { FilingStatus } from './Form1040'; -import Form1099B, { GainType } from './Form1099B'; +import Form1099B, { Form1099BInput } from './Form1099B'; import Form8949, { Form8949Box, Form8949Total } from './Form8949'; import TaxReturn from './TaxReturn'; @@ -17,14 +17,27 @@ describe('single form', () => { const tr = new TaxReturn(); tr.addPerson(p); tr.addForm(new Form1040({ filingStatus: FilingStatus.Single })); + + const fieldMap: { [key: string]: keyof Form1099BInput } = { + [Form8949Box.A]: 'shortTermBasisReported', + [Form8949Box.B]: 'shortTermBasisUnreported', + [Form8949Box.C]: 'shortTermUnreported', + [Form8949Box.D]: 'longTermBasisReported', + [Form8949Box.E]: 'longTermBasisUnreported', + [Form8949Box.F]: 'longTermUnreported', + }; + const field = fieldMap[box]; + 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), + [field]: [ + { + description: '10 shares', + proceeds: 100, + costBasis: 110, + } + ], })); const form = new Form8949(); @@ -60,29 +73,25 @@ test('multiple forms', () => { 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, + shortTermBasisReported: [ + { + description: '10 SCHB', + proceeds: 55, + costBasis: 50, + } + ], + longTermBasisUnreported: [ + { + description: '10 SCHB', + proceeds: 55, + costBasis: 50, + }, + { + description: '10 SCHF', + proceeds: 22.40, + costBasis: 10.10, + } + ], })); const form = new Form8949(); @@ -118,39 +127,33 @@ test('adjustments', () => { const b1 = new Form1099B({ payer: 'Brokerage', payee: p, - description: '10 SCHB', - proceeds: 55, - costBasis: 50, - gainType: GainType.ShortTerm, - basisReportedToIRS: false, + shortTermBasisUnreported: [ + { + description: '10 SCHB', + proceeds: 55, + costBasis: 50, + adjustments: -10, + } + ], + longTermBasisUnreported: [ + { + description: '10 SCHB', + proceeds: 18, + costBasis: 25, + adjustments: 90, + } + ], + longTermBasisReported: [ + { + description: '10 SCHF', + proceeds: 22.40, + costBasis: 10.10, + } + ] }); 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, - })); - const form = new Form8949({ - adjustments: [ - { entry: b1, code: 'W', amount: -10 }, - { entry: b2, code: 'W', amount: 90 }, - ] - }); + const form = new Form8949(); tr.addForm(form); const boxB = form.getValue(tr, 'boxB'); diff --git a/src/fed2019/Form8949.ts b/src/fed2019/Form8949.ts index d47a00b..814d4e7 100644 --- a/src/fed2019/Form8949.ts +++ b/src/fed2019/Form8949.ts @@ -6,8 +6,9 @@ import * as Trace from '../core/Trace'; import { Form, Person, TaxReturn } from '../core'; import { Line, InputLine, ComputedLine, sumLineOfForms } from '../core/Line'; +import { undefinedToZero } from '../core/Math'; -import Form1099B, { GainType } from './Form1099B'; +import Form1099B, { Form1099BRow, Form1099BInput } from './Form1099B'; export enum Form8949Box { A = 'A', // Short-term transactions reported on Form(s) 1099-B showing basis was reported to the IRS @@ -18,16 +19,6 @@ export enum Form8949Box { 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 { - adjustments?: Adjustment[]; -}; - export interface Form8949Total { proceeds: number; costBasis: number; @@ -35,26 +26,6 @@ export interface Form8949Total { gainOrLoss: number; }; -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']; @@ -66,25 +37,46 @@ class Form8949Line extends Line { value(tr: TaxReturn): Form8949Total { Trace.begin(this); - const f1099bs = matching1099Bs(tr, this._box); - const proceeds = sumLineOfForms(tr, f1099bs, '1d'); - const costBasis = sumLineOfForms(tr, f1099bs, '1e'); - const f8949 = tr.getForm(Form8949); - const adjustments = !f8949.hasInput('adjustments') ? 0 : - f8949.getInput('adjustments') - .filter(a => f1099bs.includes(a.entry)) - .reduce((acc, curr) => acc + curr.amount, 0); - Trace.end(); - return { - proceeds, - costBasis, - adjustments, - gainOrLoss: proceeds - costBasis + adjustments, + + const f1099bs = tr.findForms(Form1099B); + const fieldMap: { [key: string]: keyof Form1099BInput } = { + [Form8949Box.A]: 'shortTermBasisReported', + [Form8949Box.B]: 'shortTermBasisUnreported', + [Form8949Box.C]: 'shortTermUnreported', + [Form8949Box.D]: 'longTermBasisReported', + [Form8949Box.E]: 'longTermBasisUnreported', + [Form8949Box.F]: 'longTermUnreported', + }; + const field: keyof Form1099BInput = fieldMap[this._box]; + + const value = { + proceeds: 0, + costBasis: 0, + adjustments: 0, + gainOrLoss: 0 }; + + for (const f1099b of f1099bs) { + if (!f1099b.hasInput(field)) + continue; + + const rows = f1099b.getInput(field) as Form1099BRow[]; + for (const row of rows) { + let { proceeds, costBasis, adjustments } = row; + adjustments = undefinedToZero(adjustments); + value.proceeds += proceeds; + value.costBasis += costBasis; + value.adjustments += adjustments; + value.gainOrLoss += proceeds - costBasis + adjustments; + } + } + + Trace.end(); + return value; } }; -export default class Form8949 extends Form { +export default class Form8949 extends Form { readonly name = '8949'; readonly supportsMultipleCopies = true; diff --git a/src/fed2019/Form8960.test.ts b/src/fed2019/Form8960.test.ts index 023b7ab..d45ac4e 100644 --- a/src/fed2019/Form8960.test.ts +++ b/src/fed2019/Form8960.test.ts @@ -7,7 +7,7 @@ import { Person } from '../core'; import W2 from './W2'; import Form1040, { FilingStatus } from './Form1040'; -import Form1099B, { GainType } from './Form1099B'; +import Form1099B from './Form1099B'; import Form1099DIV from './Form1099DIV'; import Form1099INT from './Form1099INT'; import Form8949 from './Form8949'; @@ -45,11 +45,13 @@ describe('net investment income tax', () => { tr.addForm(new Form1099B({ payer: 'Brokerage', payee: p, - description: '100 VTI', - proceeds: 4000, - costBasis: 3500, - gainType: GainType.LongTerm, - basisReportedToIRS: true + longTermBasisReported: [ + { + description: '100 VTI', + proceeds: 4000, + costBasis: 3500, + } + ] })); tr.addForm(new Form8949); tr.addForm(new ScheduleD); @@ -106,11 +108,13 @@ describe('no net investment income tax', () => { tr.addForm(new Form1099B({ payer: 'Brokerage', payee: p, - description: '100 VTI', - proceeds: 4000, - costBasis: 3500, - gainType: GainType.LongTerm, - basisReportedToIRS: true + longTermBasisReported: [ + { + description: '100 VTI', + proceeds: 4000, + costBasis: 3500, + } + ] })); tr.addForm(new Form8949); tr.addForm(new ScheduleD); diff --git a/src/fed2019/Form8995.test.ts b/src/fed2019/Form8995.test.ts index 48e64bf..39815f6 100644 --- a/src/fed2019/Form8995.test.ts +++ b/src/fed2019/Form8995.test.ts @@ -6,7 +6,7 @@ import { Person } from '../core'; import Form1040, { FilingStatus } from './Form1040'; -import Form1099B, { GainType } from './Form1099B'; +import Form1099B from './Form1099B'; import Form1099DIV from './Form1099DIV'; import Form8949 from './Form8949'; import Form8995REIT from './Form8995'; @@ -56,11 +56,13 @@ test('REIT QBI with Schedule D', () => { tr.addForm(new Form1099B({ payer: 'Brokerage2 ', payee: p, - description: '100 VTI', - proceeds: 230000, - costBasis: 221000, - gainType: GainType.LongTerm, - basisReportedToIRS: true + longTermBasisReported: [ + { + description: '100 VTI', + proceeds: 230000, + costBasis: 221000, + } + ] })); tr.addForm(new Form8949); tr.addForm(new ScheduleD); -- 2.22.5