Add support for Form 1098 and the mortgage interest deduction to Schedule A.
[ustaxlib.git] / src / fed2019 / Form1098.ts
1 // Copyright 2021 Blue Static <https://www.bluestatic.org>
2 // This program is free software licensed under the GNU General Public License,
3 // version 3.0. The full text of the license can be found in LICENSE.txt.
4 // SPDX-License-Identifier: GPL-3.0-only
5
6 import { TaxReturn, Form, Person } from '../core';
7 import { InputLine, ComputedLine, FormatType, sumFormLines, sumLineOfForms } from '../core/Line';
8
9 import Form1040, { FilingStatus } from './Form1040';
10
11 export interface Form1098Input {
12 recipient: string;
13 payee: Person;
14 mortgageInterestReceived: number;
15 // This is used for computing the "average mortgage balance." Consult Pub 936 and enter
16 // a different value than the one on Form 1098 to use that instead for the average
17 // balance calculations.
18 outstandingMortgagePrincipal: number;
19 mortgageOriginationDate: Date;
20 refundOfOverpaidInterest?: number;
21 mortgageInsurancePremiums?: number;
22 pointsPaidOnPurchaseOfPrincipalResidence?: number;
23 };
24
25 class Input<T extends keyof Form1098Input> extends InputLine<Form1098Input, T> {};
26
27 export class Form1098 extends Form<Form1098Input> {
28 readonly name = '1098';
29
30 readonly supportsMultipleCopies = true;
31
32 readonly includeJointPersonForms = true;
33
34 person() { return this.getInput('payee'); }
35
36 readonly lines = {
37 'recipient': new Input('recipient'),
38 'payee': new Input('payee'),
39 '1': new Input('mortgageInterestReceived'),
40 '2': new Input('outstandingMortgagePrincipal'),
41 '3': new Input('mortgageOriginationDate'),
42 '4': new Input('refundOfOverpaidInterest'),
43 '5': new Input('mortgageInsurancePremiums'),
44 '6': new Input('pointsPaidOnPurchaseOfPrincipalResidence'),
45 };
46 };
47
48 const kGrandfatheredDate = new Date('1987-10-13');
49 const kLimitationStartDate = new Date('2017-12-15');
50
51 // Pub. 936 Worksheet
52 export class MortgageInterestDeductionWorksheet extends Form {
53 readonly name = 'Mortgage Interest Deduction Worksheet';
54
55 private get1098sMatchingPredicate(tr: TaxReturn, pred: (Form1098) => boolean): Form1098[] {
56 return tr.findForms(Form1098).filter(pred);
57 }
58
59 deductibleMortgateInterest(tr: TaxReturn): number {
60 const l11 = this.getValue(tr, '11');
61 const l12 = this.getValue(tr, '12');
62 if (l11 < l12) {
63 return this.getValue(tr, '15');
64 }
65 return sumLineOfForms(tr, tr.findForms(Form1098), '1');
66 }
67
68 readonly lines = {
69 '1': new ComputedLine((tr): number => {
70 const f1098s = this.get1098sMatchingPredicate(tr, f => f.getValue(tr, '3') <= kGrandfatheredDate);
71 if (f1098s.length == 0)
72 return 0;
73 return sumLineOfForms(tr, f1098s, '2') / f1098s.length;
74 }, 'Average balance of grandfathered debt'),
75 '2': new ComputedLine((tr): number => {
76 const f1098s = this.get1098sMatchingPredicate(tr, f => {
77 const date = f.getValue(tr, '3');
78 return date >= kGrandfatheredDate && date <= kLimitationStartDate;
79 });
80 if (f1098s.length == 0)
81 return 0;
82 return sumLineOfForms(tr, f1098s, '2') / f1098s.length;
83 }, 'Average balance of pre-limitation debt'),
84 '3': new ComputedLine((tr): number => {
85 if (tr.getForm(Form1040).filingStatus == FilingStatus.MarriedFilingSeparate)
86 return 500_000;
87 return 1_000_000;
88 }),
89 '4': new ComputedLine((tr): number => Math.max(this.getValue(tr, '1'), this.getValue(tr, '3'))),
90 '5': new ComputedLine((tr): number => sumFormLines(tr, this, ['1', '2'])),
91 '6': new ComputedLine((tr): number => Math.min(this.getValue(tr, '4'), this.getValue(tr, '5'))),
92 '7': new ComputedLine((tr): number => {
93 const f1098s = this.get1098sMatchingPredicate(tr, f => f.getValue(tr, '3') > kLimitationStartDate);
94 if (f1098s.length == 0)
95 return 0;
96 return sumLineOfForms(tr, f1098s, '2') / f1098s.length;
97 }, 'Average balance of post-limitation debt'),
98 '8': new ComputedLine((tr): number => {
99 const fs = tr.getForm(Form1040).filingStatus;
100 return tr.constants.mortgatgeInterestDeduction.limit[fs];
101 }),
102 '9': new ComputedLine((tr): number => Math.max(this.getValue(tr, '6'), this.getValue(tr, '8'))),
103 '10': new ComputedLine((tr): number => sumFormLines(tr, this, ['6', '7'])),
104 '11': new ComputedLine((tr): number => Math.min(this.getValue(tr, '9'), this.getValue(tr, '10')), 'Qualified loan limit'),
105
106 '12': new ComputedLine((tr): number => sumFormLines(tr, this, ['1', '2', '7']), 'Total of all average balanaces'),
107 '13': new ComputedLine((tr): number => sumLineOfForms(tr, tr.findForms(Form1098), '1'), 'Total interest paid'),
108 '14': new ComputedLine((tr): number => this.getValue(tr, '11') / this.getValue(tr, '12'), undefined, { formatType: FormatType.Decimal }),
109 '15': new ComputedLine((tr): number => this.getValue(tr, '13') * this.getValue(tr, '14'), 'Deductible home mortgage interest'),
110 '16': new ComputedLine((tr): number => this.getValue(tr, '13') - this.getValue(tr, '15'), 'Non-deductible interest'),
111 };
112 };