}
return this._input[name];
}
+
+ hasInput<K extends keyof I>(name: K): boolean {
+ return this._input !== undefined && name in this._input;
+ }
};
export type FormClass<T extends Form<any>> = new (...args: any[]) => T;
gainType: GainType.LongTerm,
basisReportedToIRS: true
}));
- Form8949.addForms(tr, []);
+ tr.addForm(new Form8949);
tr.addForm(new ScheduleD());
tr.getForm(ScheduleD).getValue(tr, '21');
});
import Form1040, { FilingStatus } from './Form1040';
import Form1099B, { GainType } from './Form1099B';
-import Form8949, { Form8949Box } from './Form8949';
+import Form8949, { Form8949Box, Form8949Total } from './Form8949';
describe('single form', () => {
for (const box of [Form8949Box.A, Form8949Box.B, Form8949Box.D, Form8949Box.E]) {
gainType: (box == Form8949Box.A || box == Form8949Box.B) ? GainType.ShortTerm : GainType.LongTerm,
basisReportedToIRS: (box == Form8949Box.A || box == Form8949Box.D),
}));
- Form8949.addForms(tr, []);
-
- const f8949s = tr.findForms(Form8949);
- expect(f8949s.length).toBe(6);
-
- for (let form of f8949s) {
- if (form.getValue(tr, 'Box') == box) {
- expect(form.getValue(tr, '2(d)')).toBe(100);
- expect(form.getValue(tr, '2(e)')).toBe(110);
- expect(form.getValue(tr, '2(g)')).toBe(0);
- } else {
- expect(form.getValue(tr, '2(d)')).toBe(0);
- expect(form.getValue(tr, '2(e)')).toBe(0);
- expect(form.getValue(tr, '2(g)')).toBe(0);
- }
+
+ const form = new Form8949();
+ tr.addForm(form);
+
+ const allBoxes: (keyof Form8949['lines'])[] = ['boxA', 'boxB', 'boxC', 'boxD', 'boxE', 'boxF'];
+ const otherBoxes = allBoxes.filter(b => b != `box${box}`);
+ const thisBox = `box${box}` as keyof Form8949['lines'];
+
+ let total = form.getValue(tr, thisBox);
+
+ expect(total.proceeds).toBe(100);
+ expect(total.costBasis).toBe(110);
+ expect(total.adjustments).toBe(0);
+ expect(total.gainOrLoss).toBe(-10);
+
+ for (let otherBox of otherBoxes) {
+ total = form.getValue(tr, otherBox);
+ expect(total.proceeds).toBe(0);
+ expect(total.costBasis).toBe(0);
+ expect(total.adjustments).toBe(0);
+ expect(total.gainOrLoss).toBe(0);
}
});
}
gainType: GainType.LongTerm,
basisReportedToIRS: false,
}));
- Form8949.addForms(tr, []);
-
- const f8949s = tr.findForms(Form8949);
- expect(f8949s.length).toBe(6);
-
- const boxA = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.A).pop();
- expect(boxA.getValue(tr, '2(d)')).toBe(55);
- expect(boxA.getValue(tr, '2(e)')).toBe(50);
- expect(boxA.getValue(tr, '2(g)')).toBe(0);
-
- const boxE = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.E).pop();
- expect(boxE.getValue(tr, '2(d)')).toBe(77.40);
- expect(boxE.getValue(tr, '2(e)')).toBe(60.10);
- expect(boxE.getValue(tr, '2(g)')).toBe(0);
-
- const otherBoxes = f8949s.filter(f => ![Form8949Box.A, Form8949Box.E].includes(f.getValue(tr, 'Box')));
- for (const other of otherBoxes) {
- expect(other.getValue(tr, '2(d)')).toBe(0);
- expect(other.getValue(tr, '2(e)')).toBe(0);
- expect(other.getValue(tr, '2(g)')).toBe(0);
+
+ const form = new Form8949();
+ tr.addForm(form);
+
+ const boxA = form.getValue(tr, 'boxA');
+ expect(boxA.proceeds).toBe(55);
+ expect(boxA.costBasis).toBe(50);
+ expect(boxA.adjustments).toBe(0);
+ expect(boxA.gainOrLoss).toBe(5);
+
+ const boxE = form.getValue(tr, 'boxE');
+ expect(boxE.proceeds).toBe(77.40);
+ expect(boxE.costBasis).toBe(60.10);
+ expect(boxE.adjustments).toBe(0);
+ expect(boxE.gainOrLoss).toBeCloseTo(17.3);
+
+ const otherBoxes: (keyof Form8949['lines'])[] = ['boxB', 'boxC', 'boxD', 'boxF'];
+ for (const otherBox of otherBoxes) {
+ const other = form.getValue(tr, otherBox);
+ expect(other.proceeds).toBe(0);
+ expect(other.costBasis).toBe(0);
+ expect(other.adjustments).toBe(0);
+ expect(other.gainOrLoss).toBe(0);
}
});
gainType: GainType.LongTerm,
basisReportedToIRS: true,
}));
- Form8949.addForms(tr, [
- { entry: b1, code: 'W', amount: -10 },
- { entry: b2, code: 'W', amount: 90 },
- ]);
-
- const f8949s = tr.findForms(Form8949);
- expect(f8949s.length).toBe(6);
-
- const boxA = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.B).pop();
- expect(boxA.getValue(tr, '2(d)')).toBe(55);
- expect(boxA.getValue(tr, '2(e)')).toBe(50);
- expect(boxA.getValue(tr, '2(g)')).toBe(-10);
-
- const boxD = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.D).pop();
- expect(boxD.getValue(tr, '2(d)')).toBe(22.40);
- expect(boxD.getValue(tr, '2(e)')).toBe(10.10);
- expect(boxD.getValue(tr, '2(g)')).toBe(0);
-
- const boxE = f8949s.filter(f => f.getValue(tr, 'Box') == Form8949Box.E).pop();
- expect(boxE.getValue(tr, '2(d)')).toBe(18);
- expect(boxE.getValue(tr, '2(e)')).toBe(25);
- expect(boxE.getValue(tr, '2(g)')).toBe(90);
-
- const otherBoxes = f8949s.filter(f => ![Form8949Box.B, Form8949Box.D, Form8949Box.E].includes(f.getValue(tr, 'Box')));
- for (const other of otherBoxes) {
- expect(other.getValue(tr, '2(d)')).toBe(0);
- expect(other.getValue(tr, '2(e)')).toBe(0);
- expect(other.getValue(tr, '2(g)')).toBe(0);
+
+ const form = new Form8949({
+ adjustments: [
+ { entry: b1, code: 'W', amount: -10 },
+ { entry: b2, code: 'W', amount: 90 },
+ ]
+ });
+ tr.addForm(form);
+
+ const boxB = form.getValue(tr, 'boxB');
+ expect(boxB.proceeds).toBe(55);
+ expect(boxB.costBasis).toBe(50);
+ expect(boxB.adjustments).toBe(-10);
+ expect(boxB.gainOrLoss).toBe(-5);
+
+ const boxE = form.getValue(tr, 'boxE');
+ expect(boxE.proceeds).toBe(18);
+ expect(boxE.costBasis).toBe(25);
+ expect(boxE.adjustments).toBe(90);
+ expect(boxE.gainOrLoss).toBe(83);
+
+ const boxD = form.getValue(tr, 'boxD');
+ expect(boxD.proceeds).toBe(22.40);
+ expect(boxD.costBasis).toBe(10.10);
+ expect(boxD.adjustments).toBe(0);
+ expect(boxD.gainOrLoss).toBeCloseTo(12.30);
+
+ const otherBoxes: (keyof Form8949['lines'])[] = ['boxA', 'boxC', 'boxF'];
+ for (const otherBox of otherBoxes) {
+ const other = form.getValue(tr, otherBox);
+ expect(other.proceeds).toBe(0);
+ expect(other.costBasis).toBe(0);
+ expect(other.adjustments).toBe(0);
+ expect(other.gainOrLoss).toBe(0);
}
});
};
export interface Form8949Input {
- box: Form8949Box;
adjustments?: Adjustment[];
};
+export interface Form8949Total {
+ proceeds: number;
+ costBasis: number;
+ adjustments: number;
+ gainOrLoss: number;
+};
+
function matching1099Bs(tr: TaxReturn, box: Form8949Box): Form1099B[] {
return tr.findForms(Form1099B).filter(f => {
const gainType: GainType = f.getValue(tr, '2');
});
}
-class Form8949Line extends Line<number> {
- private _box: Form8949Box;
- private _line: keyof Form1099B['lines'];
-
- constructor(f8949Functor: () => Form8949, line: keyof Form1099B['lines'], description: string) {
- const box = f8949Functor().getInput('box');
- super(`Form 8949 Box ${box} total of 1099-B ${line} (${description})`);
- this._box = box;
- this._line = line;
- }
-
- value(tr: TaxReturn): number {
- const f1099bs = matching1099Bs(tr, this._box);
- return sumLineOfForms(tr, f1099bs, this._line);
- }
-};
-
-export default class Form8949 extends Form<Form8949['_lines'], Form8949Input> {
- readonly name = '8949';
-
- readonly supportsMultipleCopies = true;
-
- protected readonly _lines = {
- 'Box': new InputLine<Form8949Input>('box'),
- '2(d)': new Form8949Line(() => this, '1d', 'proceeds'),
- '2(e)': new Form8949Line(() => this, '1e', 'cost'),
- '2(g)': new ComputedLine((tr: TaxReturn): number => {
- const f1099bs = matching1099Bs(tr, this.getInput('box'));
- const adjustments = this.getInput('adjustments').filter((a: Adjustment): boolean => {
- return f1099bs.includes(a.entry);
- });
- return adjustments.reduce((acc, curr) => acc + curr.amount, 0);
- }, 'adjustments')
- };
-
- static addForms(tr: TaxReturn, adjustments: Adjustment[]) {
- tr.addForm(new Form8949({ box: Form8949Box.A, adjustments }));
- tr.addForm(new Form8949({ box: Form8949Box.B, adjustments }));
- tr.addForm(new Form8949({ box: Form8949Box.C, adjustments }));
- tr.addForm(new Form8949({ box: Form8949Box.D, adjustments }));
- tr.addForm(new Form8949({ box: Form8949Box.E, adjustments }));
- tr.addForm(new Form8949({ box: Form8949Box.F, adjustments }));
- }
-};
-
-/*
-export interface Adjustment {
- entry: Form1099B;
- code: string;
- amount: number;
-};
-
-export interface Form8949Input {
- adjustments?: []Adjustment;
-};
-
-export interface Form8949Total {
- proceeds: number;
- costBasis: number;
- adjustmentAmount: number;
- gainOrLoss: number;
-};
-
class Form8949Line extends Line<Form8949Total> {
private _box: Form8949Box;
+ private _line: keyof Form1099B['lines'];
- constructor(description: string, box: Form8949Input) {
- super(description);
+ constructor(box: Form8949Box) {
+ super(`Form 8949 Box ${box} Total`);
this._box = box;
}
value(tr: TaxReturn): Form8949Total {
- const lineShortTerm = this._box == Form8949Box.A || this._box == Form8949Box.B || this._box == Form8949Box.C;
- const lineBasisReported = this._box == Form8949Box.A || this._box == Form8949Box.D;
-
- const f1099bs = tr.findForms(Form1099B);
- const relevant1099bs: Form1099B[] = [];
- for (const form of f1099bs) {
- const gainType = form.getValue(tr, '2');
- const basisReported = form.getValue(tr, '12');
-
- if (lineBasisReported != basisReported)
- continue;
-
- if (gainType == GainType.ShortTerm && lineShortTerm) {
- relevant1099bs.push(form);
- } else if (gainType == GainType.LongTerm && !lineShortTerm) {
- relevant1099bs.push(form);
- }
- }
-
- const sumValues = (line: keyof Form1099B['lines']) =>
- relevant1099bs.map((f: Form1099B): number => f.getValue(tr, line))
- .reduce((acc, curr) => acc + curr, 0);
-
- const proceeds = sumValues('1d');
- const costBasis = sumValues('1e');
-
+ 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);
return {
proceeds,
costBasis,
- adjustmentAmount: 0,
- gainOrLoss: costBasis - proceeds,
+ adjustments,
+ gainOrLoss: proceeds - costBasis + adjustments,
};
}
};
export default class Form8949 extends Form<Form8949['_lines'], Form8949Input> {
readonly name = '8949';
+ readonly supportsMultipleCopies = true;
+
protected readonly _lines = {
- boxATotals: new Form8949Line('Short-term basis reported', Form8949Box.A),
- boxBTotals: new Form8949Line('Short-term basis NOT reported', Form8949Box.B),
- boxCTotals: new Form8949Line('Short-term unreported', Form8949Box.C),
- boxDTotals: new Form8949Line('Long-term basis reported', Form8949Box.D),
- boxETotals: new Form8949Line('Long-term basis NOT reported', Form8949Box.E),
- boxFTotals: new Form8949Line('Long-term unreported', Form8949Box.F)
+ 'boxA': new Form8949Line(Form8949Box.A),
+ 'boxB': new Form8949Line(Form8949Box.B),
+ 'boxC': new Form8949Line(Form8949Box.C),
+ 'boxD': new Form8949Line(Form8949Box.D),
+ 'boxE': new Form8949Line(Form8949Box.E),
+ 'boxF': new Form8949Line(Form8949Box.F),
};
};
-*/
import Form1099DIV from './Form1099DIV';
import Form1040, { FilingStatus } from './Form1040';
-class ScheduleDTotal extends Line<number> {
- private _line: keyof Form8949['lines'];
- private _box: Form8949Box;
-
- constructor(description: string, line: keyof Form8949['lines'], box: Form8949Box) {
- super(description);
- this._line = line;
- this._box = box;
- }
-
- value(tr: TaxReturn): number {
- const forms: Form8949[] = tr.findForms(Form8949).filter(f => f.getValue(tr, 'Box') == this._box);
- return sumLineOfForms(tr, forms, this._line);
- }
-};
-
export default class ScheduleD extends Form<ScheduleD['_lines']> {
readonly name = 'Schedule D';
protected readonly _lines = {
// 1a not supported (Totals for all short-term transactions reported on Form 1099-B for which basis was reported to the IRS and for which you have no adjustments)
-
- '1b(d)': new ScheduleDTotal('Proceeds of short-term basis reported', '2(d)', Form8949Box.A),
- '1b(e)': new ScheduleDTotal('Cost basis of short-term basis-reported', '2(e)', Form8949Box.A),
- '1b(g)': new ScheduleDTotal('Adjustments to short-term basis-reported', '2(g)', Form8949Box.A),
- '1b(h)': new ComputedLine((tr: TaxReturn): number => {
- return (this.getValue(tr, '1b(d)') - this.getValue(tr, '1b(e)')) + this.getValue(tr, '1b(g)');
- }, 'Gain of short-term basis reported'),
-
- '2(d)': new ScheduleDTotal('Proceeds of short-term basis NOT reported', '2(d)', Form8949Box.B),
- '2(e)': new ScheduleDTotal('Cost basis of short-term NOT basis-reported', '2(e)', Form8949Box.B),
- '2(g)': new ScheduleDTotal('Adjustments to short-term NOT basis-reported', '2(g)', Form8949Box.B),
- '2(h)': new ComputedLine((tr: TaxReturn): number => {
- return (this.getValue(tr, '2(d)') - this.getValue(tr, '2(e)')) + this.getValue(tr, '2(g)');
- }, 'Gain of short-term basis NOT reported'),
-
- '3(d)': new ScheduleDTotal('Proceeds of short-term basis unreported', '2(d)', Form8949Box.C),
- '3(e)': new ScheduleDTotal('Cost basis of short-term unreported', '2(e)', Form8949Box.C),
- '3(g)': new ScheduleDTotal('Adjustments to short-term unreported', '2(g)', Form8949Box.C),
- '3(h)': new ComputedLine((tr: TaxReturn): number => {
- return (this.getValue(tr, '3(d)') - this.getValue(tr, '3(e)')) + this.getValue(tr, '3(g)');
- }, 'Gain of short-term unreported'),
-
// 4 is not supported (Short-term gain from Form 6252 and short-term gain or (loss) from Forms 4684, 6781, and 8824)
// 5 is not supported (Net short-term gain or (loss) from partnerships, S corporations, estates, and trusts from Schedule(s) K-1)
// 6 is not supported (Short-term capital loss carryover. Enter the amount, if any, from line 8 of your Capital Loss Carryover Worksheet in the instructions)
'7': new ComputedLine((tr: TaxReturn): number => {
+ // 1-3 are computed by Form8949.
// 4-6 should be included.
- return this.getValue(tr, '1b(h)') + this.getValue(tr, '2(h)'), this.getValue(tr, '3(h)');
+ const f8949 = tr.getForm(Form8949);
+ return f8949.getValue(tr, 'boxA').gainOrLoss +
+ f8949.getValue(tr, 'boxB').gainOrLoss +
+ f8949.getValue(tr, 'boxC').gainOrLoss;
}, 'Net short-term capital gain or (loss)'),
// 8a is not supported.
- '8b(d)': new ScheduleDTotal('Proceeds of long-term basis reported', '2(d)', Form8949Box.D),
- '8b(e)': new ScheduleDTotal('Cost basis of long-term basis-reported', '2(e)', Form8949Box.D),
- '8b(g)': new ScheduleDTotal('Adjustments to long-term basis-reported', '2(g)', Form8949Box.D),
- '8b(h)': new ComputedLine((tr: TaxReturn): number => {
- return (this.getValue(tr, '8b(d)') - this.getValue(tr, '8b(e)')) + this.getValue(tr, '8b(g)');
- }, 'Gain of long-term basis reported'),
-
- '9(d)': new ScheduleDTotal('Proceeds of long-term basis NOT reported', '2(d)', Form8949Box.E),
- '9(e)': new ScheduleDTotal('Cost basis of long-term NOT basis-reported', '2(e)', Form8949Box.E),
- '9(g)': new ScheduleDTotal('Adjustments to long-term NOT basis-reported', '2(g)', Form8949Box.E),
- '9(h)': new ComputedLine((tr: TaxReturn): number => {
- return (this.getValue(tr, '9(d)') - this.getValue(tr, '9(e)')) + this.getValue(tr, '9(g)');
- }, 'Gain of long-term basis NOT reported'),
-
- '10(d)': new ScheduleDTotal('Proceeds of long-term basis unreported', '2(d)', Form8949Box.F),
- '10(e)': new ScheduleDTotal('Cost basis of long-term unreported', '2(e)', Form8949Box.F),
- '10(g)': new ScheduleDTotal('Adjustments to long-term unreported', '2(g)', Form8949Box.F),
- '10(h)': new ComputedLine((tr: TaxReturn): number => {
- return (this.getValue(tr, '10(d)') - this.getValue(tr, '10(e)')) + this.getValue(tr, '10(g)');
- }, 'Gain of long-term unreported'),
-
// 11 is not supported (Gain from Form 4797, Part I; long-term gain from Forms 2439 and 6252; and long-term gain or (loss) from Forms 4684, 6781, and 8824)
// 12 is not supported (Net long-term gain or (loss) from partnerships, S corporations, estates, and trusts from Schedule(s) K-1)
'15': new ComputedLine((tr: TaxReturn): number => {
// 11-14 should be included.
- return this.getValue(tr, '8b(h)') + this.getValue(tr, '9(h)') + this.getValue(tr, '10(h)') +
- this.getValue(tr, '13');
+ const f8949 = tr.getForm(Form8949);
+ return f8949.getValue(tr, 'boxD').gainOrLoss +
+ f8949.getValue(tr, 'boxE').gainOrLoss +
+ f8949.getValue(tr, 'boxF').gainOrLoss +
+ this.getValue(tr, '13');
}, 'Net long-term capital gain or (loss)'),
'16': new ComputedLine((tr: TaxReturn): number => {