Add SALT worksheet for determining taxable amount of refunds.
authorRobert Sesek <rsesek@bluestatic.org>
Sun, 22 Mar 2020 20:40:56 +0000 (16:40 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Sun, 22 Mar 2020 20:40:56 +0000 (16:40 -0400)
src/fed2019/Schedule1.test.ts
src/fed2019/Schedule1.ts
src/fed2019/index.ts

index 7a982d4dc0a964e3e17c0e83f7472bf5f0bad733..9daa2302785c63f41194c1d39731fe8641858b13 100644 (file)
@@ -7,22 +7,57 @@ import { Person } from '../core';
 import { UnsupportedFeatureError } from '../core/Errors';
 
 import Form1040, { FilingStatus } from './Form1040';
-import Schedule1, { Schedule1Input } from './Schedule1';
+import Schedule1, { Schedule1Input, SALTWorksheet } from './Schedule1';
 import TaxReturn from './TaxReturn';
 
-test('state tax refund', () => {
+test('non-taxable state tax refund', () => {
   const p = Person.self('A');
   const tr = new TaxReturn();
   tr.addForm(new Form1040({
     filingStatus: FilingStatus.Single
   }));
+  const w = new SALTWorksheet({
+    prevYearSalt: 90000,
+    limitedPrevYearSalt: 10000,
+    prevYearItemizedDeductions: 25000,
+    prevYearFilingStatus: FilingStatus.Single
+  });
+  tr.addForm(w);
+  const f = new Schedule1({
+    stateAndLocalTaxableRefunds: 17000
+  });
+  tr.addForm(f);
+
+  expect(w.getValue(tr, '1')).toBe(17000);
+  expect(w.getValue(tr, '2')).toStrictEqual([80000, true]);
+  expect(w.getValue(tr, '3')).toBe(0);
+  expect(w.getValue(tr, '9')).toBe(0);
+  expect(tr.getForm(Form1040).getValue(tr, '7a')).toBe(0);
+});
+
+test('taxable state tax refund', () => {
+  const p = Person.self('A');
+  const tr = new TaxReturn();
+  tr.addForm(new Form1040({
+    filingStatus: FilingStatus.Single
+  }));
+  const w = new SALTWorksheet({
+    prevYearSalt: 7500,
+    limitedPrevYearSalt: 7500,
+    prevYearItemizedDeductions: 14000,
+    prevYearFilingStatus: FilingStatus.Single
+  });
+  tr.addForm(w);
   const f = new Schedule1({
-    stateAndLocalTaxableRefunds: 500
+    stateAndLocalTaxableRefunds: 2000
   });
   tr.addForm(f);
 
-  expect(f.getValue(tr, '9')).toBe(500);
-  expect(tr.getForm(Form1040).getValue(tr, '7a')).toBe(500);
+  expect(w.getValue(tr, '1')).toBe(2000);
+  expect(w.getValue(tr, '2')).toStrictEqual([2000, false]);
+  expect(w.getValue(tr, '3')).toBe(2000);
+  expect(w.getValue(tr, '9')).toBe(2000);
+  expect(tr.getForm(Form1040).getValue(tr, '7a')).toBe(2000);
 });
 
 test('unsupported inputs', () => {
index 60544cd47970cb25cbeecaf588029d3a12cf125f..852633d428e685059073789e1e9cab4411d059f6 100644 (file)
@@ -9,7 +9,7 @@ import * as Trace from '../core/Trace';
 import { NotFoundError, UnsupportedFeatureError } from '../core/Errors';
 import { undefinedToZero } from '../core/Math';
 
-import Form1040 from './Form1040';
+import Form1040, { FilingStatus } from './Form1040';
 
 export interface Schedule1Input {
   // Additional Income
@@ -63,7 +63,11 @@ export default class Schedule1 extends Form<Schedule1['_lines'], Schedule1Input>
 
   readonly _lines = {
     // Part 1
-    '1': new Input('stateAndLocalTaxableRefunds'),
+    '1': new ComputedLine((tr): number => {
+      if (this.hasInput('stateAndLocalTaxableRefunds'))
+        return tr.getForm(SALTWorksheet).getValue(tr, '9');
+      return 0;
+    }, 'Taxable refunds, credits, or offsets of state and local income taxes'),
     '2': new Input('alimonyReceived'),
     '3': new Input('businessIncome', (value: number) => {
       if (value !== undefined)
@@ -138,3 +142,61 @@ export default class Schedule1 extends Form<Schedule1['_lines'], Schedule1Input>
     }),
   };
 };
+
+export interface SALTWorksheetInput {
+  prevYearSalt: number;  // ScheduleA@5d.
+  limitedPrevYearSalt: number;  // ScheduleA@5e.
+  prevYearItemizedDeductions?: number;  // ScheduleA@17.
+  prevYearFilingStatus?: FilingStatus;
+};
+
+export class SALTWorksheet extends Form<SALTWorksheet['_lines'], SALTWorksheetInput> {
+  readonly name = 'State and Local Income Tax Refund Worksheet';
+
+  protected readonly _lines = {
+    '1': new ComputedLine((tr): number => {
+      const refunds = tr.findForm(Schedule1).getInput('stateAndLocalTaxableRefunds');
+      const prevYear = this.getInput('prevYearSalt');
+      return refunds > prevYear ? prevYear : refunds;
+    }, 'Tax refunds'),
+    '2': new ComputedLine((tr): [number, boolean] => {
+      const prevYearSalt = this.getInput('prevYearSalt');
+      const limitedPrevYearSalt = this.getInput('limitedPrevYearSalt');
+      if (prevYearSalt > limitedPrevYearSalt) {
+        return [prevYearSalt - limitedPrevYearSalt, true];
+      }
+      return [this.getValue(tr, '1'), false];
+    }, 'Remainder of SALT limitation'),
+    '3': new ComputedLine((tr): number => {
+      const l1 = this.getValue(tr, '1');
+      const l2 = this.getValue(tr, '2');
+      if (!l2[1])
+        return l1;
+      if (l1 > l2[0])
+        return l2[0] - l1;
+      // Else: none of refund is taxable.
+      return 0;
+    }),
+    '4': new InputLine<SALTWorksheetInput>('prevYearItemizedDeductions'),
+    '5': new ComputedLine((tr): number => {
+      switch (this.getInput('prevYearFilingStatus')) {
+        case FilingStatus.Single:
+        case FilingStatus.MarriedFilingSeparate:
+          return 12000;
+        case FilingStatus.MarriedFilingJoint:
+          return 24000;
+      }
+    }, 'Previous year standard deduction'),
+    '6': new ComputedLine((tr): number => 0, 'Special situations'),  // Not supported
+    '7': new ComputedLine((tr): number => this.getValue(tr, '5') + this.getValue(tr, '6')),
+    '8': new ComputedLine((tr): number => {
+      const l4 = this.getValue(tr, '4');
+      const l7 = this.getValue(tr, '7');
+      if (l7 < l4)
+        return l4 - l7;
+      // Else: none of refund is taxable.
+      return 0;
+    }),
+    '9': new ComputedLine((tr): number => Math.min(this.getValue(tr, '3'), this.getValue(tr, '8')), 'Taxable refund')
+  };
+};
index 6c92fec947a04bd6ad16a0b2b995db9a42fc9c8d..1f9f7bb26db2f85754081b97f036f13d49b53f41 100644 (file)
@@ -26,5 +26,6 @@ export * from './Form1099B';
 export * from './Form1099R';
 export * from './Form1116';
 export * from './Form8949';
+export * from './Schedule1';
 export * from './ScheduleD';
 export * from './W2';