forked from ustaxes/UsTaxes
/
F8889.ts
289 lines (270 loc) · 11.2 KB
/
F8889.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
import { Information, Person, HealthSavingsAccount } from 'ustaxes/core/data'
import { sumFields } from 'ustaxes/core/irsForms/util'
import Form, { FormTag } from 'ustaxes/core/irsForms/Form'
import F8853 from './F8853'
import { CURRENT_YEAR, healthSavingsAccounts } from '../data/federal'
export const needsF8889 = (state: Information, person: Person): boolean => {
return state.healthSavingsAccounts.some(
(h) => h.personRole === person.role || h.coverageType === 'family'
)
}
type PerMonthContributionType = {
amount: Array<number>
type: Array<'self-only' | 'family'>
}
export default class F8889 extends Form {
tag: FormTag = 'f8889'
sequenceIndex = 52
// these should only be the HSAs that belong to this person
// the person can be either the primary person or the spouse
hsas: HealthSavingsAccount<Date>[]
f8853?: F8853
person: Person
state: Information
calculatedCoverageType: 'self-only' | 'family'
perMonthContributions: PerMonthContributionType
readonly firstDayOfLastMonth: Date
constructor(state: Information, person: Person, f8853?: F8853) {
super()
this.f8853 = f8853
this.person = person
this.state = state
// The relevant HSAs are the ones either for this person or any that
// have family coverage.
this.hsas = state.healthSavingsAccounts
.filter((h) => {
if (h.personRole == person.role || h.coverageType == 'family') {
return true
}
return false
})
.map((h) => {
return {
...h,
startDate: new Date(h.startDate),
endDate: new Date(h.endDate)
}
})
this.calculatedCoverageType = 'self-only'
this.firstDayOfLastMonth = new Date(CURRENT_YEAR, 11, 1)
this.perMonthContributions = {
amount: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
type: new Array(12)
}
}
calculatePerMonthLimits = (): void => {
for (
let index = 0;
index < this.perMonthContributions.amount.length;
index++
) {
// for each month check each HSA to see if we are covered.
this.hsas.forEach((h) => {
const firstDayOfThisMonth = new Date(CURRENT_YEAR, index, 1)
if (
h.startDate <= firstDayOfThisMonth &&
h.endDate >= firstDayOfThisMonth
) {
// the coverage limit for that month is based on the type of coverage of the
// HSA. If you have both types of HSA coverage for that month, then the family
// coverage limit wins out. Since family coverage limit is higher we can just
// take the max of the coverage limit for this month.
if (
this.perMonthContributions.amount[index] <
healthSavingsAccounts.contributionLimit[h.coverageType]
) {
this.perMonthContributions.amount[index] =
healthSavingsAccounts.contributionLimit[h.coverageType]
this.perMonthContributions.type[index] = h.coverageType
}
}
})
}
// The calculated coverage type is whichever one was in effect for longer
let familyMonthCount = 0
let singleMonthCount = 0
this.perMonthContributions.amount.forEach((m) => {
if (m == healthSavingsAccounts.contributionLimit.family) {
familyMonthCount += 1
} else if (m == healthSavingsAccounts.contributionLimit['self-only']) {
singleMonthCount += 1
}
})
if (familyMonthCount >= singleMonthCount) {
this.calculatedCoverageType = 'family'
} else {
this.calculatedCoverageType = 'self-only'
}
}
/* If you are an eligible individual on the first day of the last month of your tax year
(December 1 for most taxpayers), you are considered to be an eligible individual
for the entire year.
*/
lastMonthRule = (): boolean => {
return this.hsas.some((hsa) => hsa.endDate >= this.firstDayOfLastMonth)
}
/*If, on the first day of the last month of your tax year (December 1 for most taxpayers),
you had family coverage, check the "family" box.
*/
lastMonthCoverage = (): string | undefined => {
let coverage = undefined
for (const hsa of this.hsas) {
if (hsa.endDate >= this.firstDayOfLastMonth) {
if (hsa.coverageType == 'family') {
coverage = 'family'
break
}
coverage = 'self-only'
}
}
return coverage
}
fullYearHsa = (): boolean => {
return this.hsas.some(
(hsa) =>
hsa.startDate <= new Date(CURRENT_YEAR, 0, 1) &&
hsa.endDate >= this.firstDayOfLastMonth
)
}
contributionLimit = (): number => {
/*If you were under age 55 at the end of 2020 and, on the first day of every month during 2020,
you were, or were considered, an eligible individual with the same coverage, enter $3,550
($7,100 for family coverage). All others, see the instructions for the amount to enter.
*/
/*If the last-month rule (see Last-month rule, earlier) applies, you are considered an eligible individual
for the entire year. You are treated as having the same HDHP coverage for the entire year as you had on
the first day of the last month of your tax year.
*/
if (this.lastMonthRule()) {
// If, on the first day of the last month of your tax year (December 1 for most taxpayers),
// you had family coverage, check the "family" box.
const lastMonthCoverage = this.lastMonthCoverage()
if (lastMonthCoverage !== undefined) {
if (lastMonthCoverage === 'family') {
this.calculatedCoverageType = 'family'
return healthSavingsAccounts.contributionLimit.family
} else if (lastMonthCoverage === 'self-only') {
this.calculatedCoverageType = 'self-only'
return healthSavingsAccounts.contributionLimit['self-only']
}
}
}
/* If you don't have coverage in the last month, then you need to figure out
your contribution limit. If you don't have coverage for that month then
your contribution limit is 0. So let's initialize our per-month contribution
limit based on that.
*/
this.calculatePerMonthLimits()
return Math.round(
this.perMonthContributions.amount.reduce((a, b) => a + b) / 12
)
}
splitFamilyContributionLimit = (): number | undefined => {
/* if you and your spouse each have separate HSAs and had family coverage under an HDHP at any time during 2020*/
/* If you are treated as having family coverage for each month, divide the amount on line 5 equally between you
and your spouse, unless you both agree on a different allocation (such as allocating nothing to one spouse).
Enter your allocable share on line 6.*/
/* Example. In 2020, you are an eligible individual and have self-only HDHP coverage. In March you marry and as
of April 1 you have family HDHP coverage. Neither you nor your spouse qualify for the additional contribution
amount. Your spouse has a separate HSA and is an eligible individual from April 1 to December 31, 2020.
Because you and your spouse are considered to have family coverage on December 1, your contribution limit is
$7,100 (the family coverage maximum). You and your spouse can divide this amount in any allocation to which
you agree (such as allocating nothing to one spouse).*/
if (!this.hsas.some((h) => h.coverageType === 'family')) {
return this.l5()
}
if (this.lastMonthCoverage() === 'family') {
// TODO: This hard codes the allocation at 50% for each spouse but the
// rules say any contribution allowcation is allowed
return Math.round(this.l5() ?? 0 / 2)
} else {
// get the number of months of family coverage
const familyMonths: number = this.perMonthContributions.type.filter(
(t) => t === 'family'
).length
// TODO: This hard codes the allocation at 50% for each spouse but the
// rules say any contribution allowcation is allowed
const familyContribution: number =
(familyMonths * healthSavingsAccounts.contributionLimit['family']) /
12 /
2
// Add this to the contributions of the self-only portion of the year
const selfMonths: number = 12 - familyMonths
const selfContribution: number =
(selfMonths * healthSavingsAccounts.contributionLimit['self-only']) / 12
return familyContribution + selfContribution
}
}
/*Include on line 2 only those amounts you, or others on your behalf, contributed to your HSA in 2020.
Also, include those contributions made from January 1, 2021, through April 15, 2021, that were for 2020.
Do not include employer contributions (see line 9) or amounts rolled over from another HSA or Archer MSA.
See Rollovers, earlier. Also, do not include any qualified HSA funding distributions (see line 10).
Contributions to an employee's account through a cafeteria plan are treated as employer contributions
and are not included on line 2.
*/
l2 = (): number =>
this.hsas.reduce((total, hsa) => hsa.contributions + total, 0)
l3 = (): number => this.contributionLimit()
l4 = (): number => sumFields([this.f8853?.l1(), this.f8853?.l2()])
l5 = (): number => (this.l3() ?? 0) - this.l4()
l6 = (): number | undefined => this.splitFamilyContributionLimit()
// TODO: Additional contirbution amount. Need to know the age of the user
l7 = (): number | undefined => undefined
l8 = (): number => sumFields([this.l6(), this.l7()])
// Employer contributions are listed in W2 box 12 with code W
l9 = (): number =>
this.state.w2s.reduce((res, w2) => res + (w2.box12?.W ?? 0), 0)
l10 = (): number | undefined => undefined
l11 = (): number => sumFields([this.l9(), this.l10()])
l12 = (): number => {
const tmp = this.l8() - this.l11()
return tmp < 0 ? 0 : tmp
}
l13 = (): number | undefined =>
this.l2() < this.l12() ? this.l2() : this.l12()
l14a = (): number =>
this.hsas.reduce((total, hsa) => hsa.totalDistributions + total, 0)
l14b = (): number | undefined => undefined
l14c = (): number => this.l14a() - (this.l14b() ?? 0)
l15 = (): number =>
this.hsas.reduce((total, hsa) => hsa.qualifiedDistributions + total, 0)
l16 = (): number => Math.max(0, this.l14c() - this.l15())
l17a = (): boolean => false
// TODO: add in logic for when line 17a is true
l17b = (): number | undefined => Math.round(this.l16() * 0.2)
l18 = (): number | undefined => undefined
l19 = (): number | undefined => undefined
l20 = (): number => sumFields([this.l18(), this.l19()])
l21 = (): number => Math.round(this.l20() * 0.1)
fields = (): Array<string | number | boolean | undefined> => {
return [
`${this.person.firstName} ${this.person.lastName}`,
this.person.ssid,
this.calculatedCoverageType === 'self-only', // line 1: self-only check box
this.calculatedCoverageType === 'family', // line 1: family checkbox
this.l2(),
this.l3(),
this.l4(),
this.l5(),
this.l6(),
this.l7(),
this.l8(),
this.l9(),
this.l10(),
this.l11(),
this.l12(),
this.l13(),
this.l14a(),
this.l14b(),
this.l14c(),
this.l15(),
this.l16(),
this.l17a(),
this.l17b(),
this.l18(),
this.l19(),
this.l20(),
this.l21()
]
}
}