Redo 1099-B to be simplerf for recording adjustments.
authorRobert Sesek <rsesek@bluestatic.org>
Sun, 22 Mar 2020 21:46:37 +0000 (17:46 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Sun, 22 Mar 2020 21:46:37 +0000 (17:46 -0400)
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
src/fed2019/Form1099B.ts
src/fed2019/Form1116.test.ts
src/fed2019/Form8949.test.ts
src/fed2019/Form8949.ts
src/fed2019/Form8960.test.ts
src/fed2019/Form8995.test.ts

index 7ae22de23cb20d958784fd8de6d172b842213c32..109b74f3c207248a558fa0f2cb6ae32096483347 100644 (file)
@@ -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());
index 3cf59ba85e2d7ade5fe32db43a7894fac6631304..816cfef9d0c3d5432112dbeb10587efeeed71186 100644 (file)
@@ -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<T extends keyof Form1099BInput> extends InputLine<Form1099BInput, T> {};
@@ -58,24 +46,11 @@ export default class Form1099B extends Form<Form1099B['_lines'], Form1099BInput>
   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'),
   };
 };
index 77089ec66e846bbe9958f685468e44a060c3a0a0..cc6eedfdc082a342ecbbbce31dac02439bada2d6 100644 (file)
@@ -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);
index 25783664032f2f006075809a492877b1ecbe3dcb..90d2ad31becefd567ffb13bdd6bd1e0dd2727f29 100644 (file)
@@ -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');
index d47a00bc4a7bda5efab658d479987f54cb743b6d..814d4e73bb52974018954e9ca019a8dddcfb6c0d 100644 (file)
@@ -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<Form8949Total> {
   private _box: Form8949Box;
   private _line: keyof Form1099B['lines'];
@@ -66,25 +37,46 @@ class Form8949Line extends Line<Form8949Total> {
 
   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<Form8949['_lines'], Form8949Input> {
+export default class Form8949 extends Form<Form8949['_lines']> {
   readonly name = '8949';
 
   readonly supportsMultipleCopies = true;
index 023b7ab2390c15fd9dc65bea3b62705392322dbe..d45ac4ef48273f5d2d3989f6cceb2b4ea491205f 100644 (file)
@@ -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);
index 48e64bf02bbe8e1c5a1a2751aa2ab9a12699ab4e..39815f62440be7a13f10cbc7c83ad8e35d6047c4 100644 (file)
@@ -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);