Simplify core imports.
[ustaxlib.git] / src / fed2019 / Form1116.ts
1 import { Form, Person, TaxReturn } from '../core';
2 import { ComputedLine, InputLine, ReferenceLine } from '../core/Line';
3 import { UnsupportedFeatureError } from '../core/Errors';
4 import { reduceBySum } from '../core/Math';
5
6 import Form1040 from './Form1040';
7 import Form8949 from './Form8949';
8 import Schedule2 from './Schedule2';
9 import ScheduleD from './ScheduleD';
10
11 export enum ForeignIncomeCategory {
12 A = 'A: Section 951A category',
13 B = 'B: Foreign branch category',
14 C = 'C: Passive category',
15 D = 'D: General category',
16 E = 'E: Section 901(j)',
17 F = 'F: Certain income re-sourced by treaty',
18 G = 'G: Lump-sum distributions',
19 };
20
21 export interface Form1116Input {
22 person: Person;
23 incomeCategory: ForeignIncomeCategory;
24 posessionName: 'RIC' | string;
25 grossForeignIncome: number;
26 lossesFromForeignSources?: number;
27 totalForeignTaxesPaidOrAccrued: number;
28 };
29
30 class Input<T extends keyof Form1116Input> extends InputLine<Form1116Input, T> {};
31
32 export default class Form1116 extends Form<Form1116['_lines'], Form1116Input> {
33 readonly name = '1116';
34
35 protected readonly _lines = {
36 'category': new ComputedLine((tr: TaxReturn): ForeignIncomeCategory => {
37 const input = this.getInput('incomeCategory');
38 if (input != ForeignIncomeCategory.C)
39 throw new UnsupportedFeatureError(`Form 1116 does not support ${input}`);
40 return input;
41 }),
42 'i': new Input('posessionName'),
43 '1a': new Input('grossForeignIncome'),
44 // 1b not supported - services as an employee.
45 // 2 not supported - Expenses definitely related to the income
46 '3a': new ReferenceLine(Form1040, '9', 'Deductions'),
47 '3b': new ComputedLine(() => 0, 'Other deductions'), // Not supported
48 '3c': new ComputedLine((tr): number => {
49 return this.getValue(tr, '3a') + this.getValue(tr, '3b');
50 }),
51 '3d': new ReferenceLine(Form1116 as any, '1a'), // Should exclude income from unsupported Form 2555.
52 '3e': new ComputedLine((tr): number => {
53 const f1040 = tr.findForm(Form1040);
54 // Take total income, but do not net capital gains out with losses, so remove
55 // line 6.
56 let grossIncome = f1040.getValue(tr, '7b') - f1040.getValue(tr, '6');
57 const f8949 = tr.findForm(Form8949);
58 if (f8949) {
59 const keys: (keyof Form8949['lines'])[] = ['boxA', 'boxB', 'boxC', 'boxD', 'boxE', 'boxF'];
60 const values = keys.map(k => f8949.getValue(tr, k).gainOrLoss).filter(n => n > 0);
61 grossIncome += reduceBySum(values);
62
63 grossIncome += tr.getForm(ScheduleD).getValue(tr, '13');
64 }
65 return grossIncome;
66 }),
67 '3f': new ComputedLine((tr): number => {
68 return Number.parseFloat((this.getValue(tr, '3d') / this.getValue(tr, '3e')).toFixed(4));
69 }),
70 '3g': new ComputedLine((tr): number => {
71 return this.getValue(tr, '3c') * this.getValue(tr, '3f');
72 }),
73 // 4 not supported - Pro rata share of interest expense
74 '5': new Input('lossesFromForeignSources', undefined, 0),
75 '6': new ComputedLine((tr): number => {
76 // Should include 2, 4a, 4b.
77 return this.getValue(tr, '3g') + this.getValue(tr, '5');
78 }),
79 '7': new ComputedLine((tr): number => this.getValue(tr, '1a') - this.getValue(tr, '6')),
80 // Skip the complicated Part II matrix and just use the input value.
81 '8': new Input('totalForeignTaxesPaidOrAccrued'),
82 '9': new ReferenceLine(Form1116 as any, '8'),
83 // 10 not supported - Carryback or carryover
84 '11': new ComputedLine((tr): number => this.getValue(tr, '9') /* + this.getValue(tr, '10') */),
85 // 12 not supported - Reduction in foreign taxes
86 // 13 not supported - Taxes reclassified under high tax kickout
87 '14': new ComputedLine((tr): number => {
88 return this.getValue(tr, '11') /*+
89 this.getValue(tr, '12') +
90 this.getValue(tr, '13')*/;
91 }),
92 '15': new ReferenceLine(Form1116 as any, '7'),
93 // 16 not supported - Adjustments to line 15
94 '17': new ComputedLine((tr): number => this.getValue(tr, '15') /* + this.getValue(tr, '16') */),
95 // TODO - This does not handle necessary adjustments.
96 '18': new ReferenceLine(Form1040, '11b'),
97 '19': new ComputedLine((tr): number => this.getValue(tr, '17') / this.getValue(tr, '18')),
98 '20': new ComputedLine((tr): number => {
99 let value = tr.getForm(Form1040).getValue(tr, '12a');
100 const sched2 = tr.findForm(Schedule2);
101 if (sched2)
102 value += sched2.getValue(tr, '2');
103 return value;
104 }),
105 '21': new ComputedLine((tr): number => this.getValue(tr, '20') * this.getValue(tr, '19'), 'Maximum amount of credit'),
106 '22': new ComputedLine((tr): number => Math.min(this.getValue(tr, '14'), this.getValue(tr, '21'))),
107 // 23-30 not supported (other category F1116)
108 '31': new ReferenceLine(Form1116 as any, '22'),
109 // 32 not supported - Reduction of credit for international boycott operations
110 '33': new ComputedLine((tr): number => this.getValue(tr, '31') /* - this.getValue(tr, '32')*/),
111 };
112 };