Skip to content
1 change: 1 addition & 0 deletions cli/store/form8990-workpaper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ async function calculateRecord(
const projected = reconcileForm8990Projection(
pending.form8990,
pending,
{ taxYear: meta.year, formType: "f1040" },
);
const taxpayerSsn = z.object({ taxpayer_ssn: z.string().min(1) })
.passthrough().parse(projected.sourceRecords.returnInputs.general)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from "zod";
import type { NodeContext } from "../../../../../../../core/types/node-context.ts";
import {
form8990LinesSchema,
publicInputSchema,
Expand Down Expand Up @@ -81,10 +82,12 @@ export function projectForm8990ForExport(
export function reconcileForm8990Projection(
raw: unknown,
allPending: Readonly<Record<string, unknown>>,
context: NodeContext,
): Form8990Projection {
const projected = projectionSchema.parse(raw);
const source = projected.sourceRecords;
const result = runBoundedForm8990TwoPass({
context,
returnInputs: source.returnInputs,
receipts: source.form8990.receipts,
interestExpenseRecords: source.form8990.interestExpenseRecords,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Form8995AInput } from "../../../../../nodes/intermediate/forms/ded
import { calculateMixedFishingQbi } from "../../../../../nodes/intermediate/forms/deductions/business/form8995a/mixed-fishing.ts";
import {
assertScheduleJSourceReturn,
executeScheduleJSourceReturn,
retainedFishingProfit,
} from "../../../taxes/income-averaging/schedule-j/schedule_j_source_return.ts";
import { assertMultiBusinessInvestmentSources } from "../../../../mef/forms/deductions/business/f8995/f8995-investment.ts";
Expand Down Expand Up @@ -94,6 +95,10 @@ export function assertMixedFishingQbiReturn(
"Mixed fishing QBI needs only the reviewed C/F and issued dividend income",
);
}
assertScheduleJSourceReturn(pending);
assertScheduleJSourceReturn(
pending,
(inputs) =>
executeScheduleJSourceReturn(inputs, { taxYear, formType: "f1040" }),
);
return calculated;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { publicInputSchema } from "../../../../../nodes/intermediate/forms/deductions/business/form8990/index.ts";
import { runBoundedForm8990TwoPass } from "../../../../../nodes/intermediate/forms/deductions/business/form8990/run-two-pass.ts";
import { execute, type ExecuteResult } from "../../../../../../../core/runtime/executor.ts";
import type { NodeContext } from "../../../../../../../core/types/node-context.ts";

/** Internal composed workpaper. Public8990 still requires authentic source and
* accepted carry persistence; this does not create an admitted export route. */
export function executeForm172Form8990Return(
inputs: Record<string, unknown>,
context: NodeContext,
executeGraph?: (
inputs: Record<string, unknown>,
context: Parameters<typeof execute>[3],
Expand All @@ -17,6 +19,7 @@ export function executeForm172Form8990Return(
Object.entries(inputs).filter(([key]) => key !== "form8990"),
);
const twoPass = runBoundedForm8990TwoPass({
context,
returnInputs,
...source,
executeGraph,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export function reconcileForm172ModifiedIncome(
true,
graph,
)
: executeForm172Form8990Return(structuredClone(inputs), graph).execution;
: executeForm172Form8990Return(structuredClone(inputs), modifiedContext, graph).execution;
if (execution.diagnostics.length) {
throw new Error("Modified NOL income needs a successful source graph");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ const interestSourceRecords = {
async function interestFixture() {
const f = await fixture();
f.inputs = { ...interestBaseInputs, form8990: interestSourceRecords };
const before = executeForm172Form8990Return(f.inputs);
const before = executeForm172Form8990Return(f.inputs, { taxYear: 2025, formType: "f1040" });
const return1040 = before.execution.pending.f1040!;
const a = f.currentReview.annual_review;
a.agi = Number(return1040.line11_agi);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ export async function stageForm172ProjectedReturn(
rawDocuments: readonly SourceDocumentBytes[],
) {
const inputs = structuredClone(rawInputs);
const nolContext = { taxYear: 2025, formType: "f1040" } as const;
const staged = await stageForm172ReviewedReturnCalculation(
inputs,
rawBinding,
rawDocuments,
inputs.form8990 !== undefined
? (source) => executeForm172Form8990Return(source).execution
? (source) => executeForm172Form8990Return(source, nolContext).execution
: undefined,
);
// These routes need their source-dependent staging composed with the NOL
Expand Down Expand Up @@ -76,7 +77,6 @@ export async function stageForm172ProjectedReturn(
}
}
const projectedRegistry = { ...registry, start: new RetainedNolStart() };
const nolContext = { taxYear: 2025, formType: "f1040" } as const;
const executeNolGraph = (
source: Record<string, unknown>,
context: Parameters<typeof execute>[3] = nolContext,
Expand All @@ -90,7 +90,7 @@ export async function stageForm172ProjectedReturn(
// Recompute return-derived adoption/education operands on every NOL pass.
const interestComposition = inputs.form8990 === undefined
? undefined
: executeForm172Form8990Return(inputs, executeNolGraph, deduction);
: executeForm172Form8990Return(inputs, nolContext, executeNolGraph, deduction);
const execution = interestComposition?.execution ??
executePreQefSourceReturn(inputs, nolContext, true, executeNolGraph);
if (execution.diagnostics.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
SourceDocumentBytes,
SourceDocumentClaim,
} from "../../../../../../../core/runtime/source-documents.ts";
import type { NodeContext } from "../../../../../../../core/types/node-context.ts";
import { buildExecutionPlan } from "../../../../../../../core/runtime/planner.ts";
import { registry } from "../../../../registry.ts";
import { inputSchema as k1InputSchema } from "../../../../../nodes/inputs/income/rental-passthrough/k1_s_corp/index.ts";
Expand All @@ -20,6 +21,7 @@ import { inspectPrior7203MeFXml } from "../../../../../nodes/intermediate/forms/
export async function executePriorReduced7203WithSourceDocuments(
inputs: Record<string, unknown>,
documents: readonly SourceDocumentBytes[],
context: NodeContext,
): Promise<
DocumentBoundExecuteResult & {
readonly stagedPriorReducedNoteGain: ReturnType<
Expand All @@ -41,6 +43,7 @@ export async function executePriorReduced7203WithSourceDocuments(
const stagedPriorReducedNoteGain = calculatePriorReducedNoteGainCandidate(
source,
k1,
context,
);
const stock = k1.form7203_stock_loss_ledger;
const general = inputs.general as Record<string, unknown> | undefined;
Expand Down Expand Up @@ -104,7 +107,7 @@ export async function executePriorReduced7203WithSourceDocuments(
buildExecutionPlan(registry),
registry,
inputs,
{ taxYear: 2025, formType: "f1040" },
context,
claims,
documents,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ import { f1040_2025 } from "../../../../index.ts";
import { normalizeAllPending } from "../../../../return-processing/pending.ts";
import { extractFilerIdentity } from "../../../../../mef/filer.ts";
import { buildPdfBytes, type PdfPageOrigin } from "../../../../pdf/builder.ts";
import { assertScheduleJSourceReturn } from "../../../taxes/income-averaging/schedule-j/schedule_j_source_return.ts";
import {
assertScheduleJSourceReturn,
executeScheduleJSourceReturn,
} from "../../../taxes/income-averaging/schedule-j/schedule_j_source_return.ts";
import { form8814CapitalScheduleJInputs } from "./form8814_capital_schedulej.fixture.ts";

const replayScheduleJ = (inputs: Record<string, unknown>) =>
executeScheduleJSourceReturn(inputs, { taxYear: 2025, formType: "f1040" });

const cases = [
{
id: "child-direct-line7a",
Expand Down Expand Up @@ -86,7 +92,7 @@ for (const expected of cases) {
{ source: "schedule_d.net_capital_gain", amount: expected.capital },
],
);
assertScheduleJSourceReturn(actual.pending);
assertScheduleJSourceReturn(actual.pending, replayScheduleJ);
const noJ: any = structuredClone(input);
delete noJ.schedule_j;
const noElection = f1040_2025.executeReturn(noJ);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ import {
scheduleJFishingInputs,
scheduleJFishingPhaseInInputs,
} from "./schedule_j_fishing_source.fixture.ts";
import { assertScheduleJSourceReturn } from "./schedule_j_source_return.ts";
import {
assertScheduleJSourceReturn,
executeScheduleJSourceReturn,
} from "./schedule_j_source_return.ts";
import { calculateMixedFishingQbi } from "../../../../../nodes/intermediate/forms/deductions/business/form8995a/mixed-fishing.ts";
import { form8995a } from "../../../../mef/forms/deductions/business/f8995a/f8995a.ts";
import { form8995aPdf } from "../../../../pdf/forms/deductions/business/f8995a.ts";

const replayScheduleJ = (inputs: Record<string, unknown>) =>
executeScheduleJSourceReturn(inputs, { taxYear: 2025, formType: "f1040" });

const evidenceDir = process.env.SCHEDULE_J_FISHING_EVIDENCE_DIR ??
".state/research/schedulej-fishing-preferential-source";
const xsd = process.env.SCHEDULE_J_FISHING_XSD_PATH ??
Expand Down Expand Up @@ -67,7 +73,7 @@ test("retained commercial catch, one/two farms and dividends refigure actual Sch
const noJ = await packet(`${kind}-without-j`, without);
const elected = await packet(kind, input);
const p = elected.pending;
assertScheduleJSourceReturn(elected.result.pending);
assertScheduleJSourceReturn(elected.result.pending, replayScheduleJ);
assertEquals(
p.schedule1.line3_schedule_c,
kind === "fishing" ? 320_000 : 120_000,
Expand Down Expand Up @@ -128,7 +134,7 @@ test("actual single-filer QBI phase-out at $247,300 leaves smaller mixed catch f
assertEquals(p.f1040.line16_income_tax, 60_162);
assertEquals(p.form6251.regular_tax, 62_451);
assertEquals(noJ.pending.form6251.regular_tax, 62_451);
assertScheduleJSourceReturn(elected.result.pending);
assertScheduleJSourceReturn(elected.result.pending, replayScheduleJ);
assertEquals(elected.xml.includes("<IRS8995A "), false);
});

Expand Down Expand Up @@ -156,7 +162,7 @@ test("actual mixed fishing and farm phase-in derives separate advanced QBI", asy
result.xml.includes("<NetCapitalGainAmt>30000</NetCapitalGainAmt>"),
true,
);
assertScheduleJSourceReturn(result.result.pending);
assertScheduleJSourceReturn(result.result.pending, replayScheduleJ);
const filer = extractFilerIdentity(result.pending.f1040)!;
for (
const mutate of [
Expand Down Expand Up @@ -219,7 +225,7 @@ test("fishing attribution rejects absent, mutated and mismatched source bytes",
assertEquals(result.diagnostics, []);
const pending: any = structuredClone(result.pending);
pending.schedule_j_calculation.fishing_net_profit += 1;
assertThrows(() => assertScheduleJSourceReturn(pending), Error);
assertThrows(() => assertScheduleJSourceReturn(pending, replayScheduleJ), Error);
const filer = extractFilerIdentity(
normalizeAllPending(result.pending).f1040,
)!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ import { scheduleJJointFishingAdvancedInputs } from "./schedule_j_joint_fishing.
import { extractFilerIdentity } from "../../../../../mef/filer.ts";
import { buildPdfBytes, type PdfPageOrigin } from "../../../../pdf/builder.ts";
import { normalizeAllPending } from "../../../../return-processing/pending.ts";
import { assertScheduleJSourceReturn } from "./schedule_j_source_return.ts";
import {
assertScheduleJSourceReturn,
executeScheduleJSourceReturn,
} from "./schedule_j_source_return.ts";
import { calculateMixedFishingQbi } from "../../../../../nodes/intermediate/forms/deductions/business/form8995a/mixed-fishing.ts";

const replayScheduleJ = (inputs: Record<string, unknown>) =>
executeScheduleJSourceReturn(inputs, { taxYear: 2025, formType: "f1040" });

const evidenceDir = process.env.SCHEDULE_J_JOINT_ADVANCED_EVIDENCE_DIR ??
".state/research/schedulej-joint-advanced-oct6/packets";
const xsd = process.env.SCHEDULE_J_JOINT_XSD_PATH ??
Expand Down Expand Up @@ -60,7 +66,7 @@ test("joint C/F above MFJ QBI threshold files two owner-specific 8995-A phase-in
const elected = await packet(`advanced-${fishingOwner}`, input);
const p = elected.pending;
const q = calculateMixedFishingQbi(p.form8995a, 2025);
assertScheduleJSourceReturn(elected.result.pending);
assertScheduleJSourceReturn(elected.result.pending, replayScheduleJ);
assertEquals(q.profits, [200_000, 300_000]);
assertEquals(q.allocations, [13_596, 14_935]);
assertEquals(q.rows.map((row) => row.lines.line2), [186_404, 285_065]);
Expand Down Expand Up @@ -124,7 +130,7 @@ test("advanced joint owner source and final filed 8995-A conflicts are rejected"
) {
const pending: any = structuredClone(result.pending);
change(pending);
assertThrows(() => assertScheduleJSourceReturn(pending));
assertThrows(() => assertScheduleJSourceReturn(pending, replayScheduleJ));
await assertRejects(() => f1040_2025.prepareReturn(pending, filer));
await assertRejects(() => buildPdfBytes(pending, filer, ".pdf-cache"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ import { scheduleJJointFishingPositiveLimitsInputs } from "./schedule_j_joint_fi
import { extractFilerIdentity } from "../../../../../mef/filer.ts";
import { buildPdfBytes, type PdfPageOrigin } from "../../../../pdf/builder.ts";
import { normalizeAllPending } from "../../../../return-processing/pending.ts";
import { assertScheduleJSourceReturn } from "./schedule_j_source_return.ts";
import {
assertScheduleJSourceReturn,
executeScheduleJSourceReturn,
} from "./schedule_j_source_return.ts";
import { calculateMixedFishingQbi } from "../../../../../nodes/intermediate/forms/deductions/business/form8995a/mixed-fishing.ts";

const replayScheduleJ = (inputs: Record<string, unknown>) =>
executeScheduleJSourceReturn(inputs, { taxYear: 2025, formType: "f1040" });

const evidenceDir = process.env.SCHEDULE_J_JOINT_POSITIVE_EVIDENCE_DIR ??
".state/research/schedulej-joint-positive-oct6/packets";
const xsd = process.env.SCHEDULE_J_JOINT_XSD_PATH ??
Expand Down Expand Up @@ -68,7 +74,7 @@ test("joint C/F wages and UBIA derive three distinct full phase-out limits", asy
const elected = await packet(`${kind}-${owner}`, input);
const p = elected.pending;
const q = calculateMixedFishingQbi(p.form8995a, 2025);
assertScheduleJSourceReturn(elected.result.pending);
assertScheduleJSourceReturn(elected.result.pending, replayScheduleJ);
assertEquals(q.profits[0], 200_000);
assertEquals(q.profits[1], wages ? 478_185 : 500_000);
assertEquals(q.rows.map((row) => row.lines.line15), [0, rowDed]);
Expand Down Expand Up @@ -146,7 +152,7 @@ test("joint positive QBI source rejects payroll, W-2, ownership, property, and f
const pending: any = structuredClone(result.pending);
pending.form8995a.mixed_fishing_qbi_source.schedule_f
.qbi_positive_limit_inventory.sha256 = "0".repeat(64);
assertThrows(() => assertScheduleJSourceReturn(pending));
assertThrows(() => assertScheduleJSourceReturn(pending, replayScheduleJ));
await assertRejects(() => f1040_2025.prepareReturn(pending, filer));
await assertRejects(() => buildPdfBytes(pending, filer, ".pdf-cache"));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import { scheduleJJointFishingInputs } from "./schedule_j_joint_fishing.fixture.
import { extractFilerIdentity } from "../../../../../mef/filer.ts";
import { buildPdfBytes, type PdfPageOrigin } from "../../../../pdf/builder.ts";
import { normalizeAllPending } from "../../../../return-processing/pending.ts";
import { assertScheduleJSourceReturn } from "./schedule_j_source_return.ts";
import {
assertScheduleJSourceReturn,
executeScheduleJSourceReturn,
} from "./schedule_j_source_return.ts";

const replayScheduleJ = (inputs: Record<string, unknown>) =>
executeScheduleJSourceReturn(inputs, { taxYear: 2025, formType: "f1040" });

const evidenceDir = process.env.SCHEDULE_J_JOINT_EVIDENCE_DIR ??
".state/research/schedulej-joint-fishing-oct6/packets";
Expand Down Expand Up @@ -58,7 +64,7 @@ test("joint primary/spouse farm and fishing source elects owner-specific Schedul
const noJ = await packet(`joint-${fishingOwner}-without-j`, noJInput);
const elected = await packet(`joint-${fishingOwner}`, input);
const p = elected.pending;
assertScheduleJSourceReturn(elected.result.pending);
assertScheduleJSourceReturn(elected.result.pending, replayScheduleJ);
assertEquals(p.f1040.filing_status, "mfj");
assertEquals(p.schedule1.line3_schedule_c, 120_000);
assertEquals(p.schedule1.line6_schedule_f, 200_000);
Expand Down Expand Up @@ -125,7 +131,7 @@ test("joint Schedule J source and owner conflicts cannot be exported", async ()
) {
const pending: any = structuredClone(result.pending);
change(pending);
assertThrows(() => assertScheduleJSourceReturn(pending));
assertThrows(() => assertScheduleJSourceReturn(pending, replayScheduleJ));
await assertRejects(() => f1040_2025.prepareReturn(pending, filer));
await assertRejects(() => buildPdfBytes(pending, filer, ".pdf-cache"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ import { scheduleJJointFishingZeroLimitInputs } from "./schedule_j_joint_fishing
import { extractFilerIdentity } from "../../../../../mef/filer.ts";
import { buildPdfBytes, type PdfPageOrigin } from "../../../../pdf/builder.ts";
import { normalizeAllPending } from "../../../../return-processing/pending.ts";
import { assertScheduleJSourceReturn } from "./schedule_j_source_return.ts";
import {
assertScheduleJSourceReturn,
executeScheduleJSourceReturn,
} from "./schedule_j_source_return.ts";
import { calculateMixedFishingQbi } from "../../../../../nodes/intermediate/forms/deductions/business/form8995a/mixed-fishing.ts";

const replayScheduleJ = (inputs: Record<string, unknown>) =>
executeScheduleJSourceReturn(inputs, { taxYear: 2025, formType: "f1040" });

const evidenceDir = process.env.SCHEDULE_J_JOINT_ZERO_EVIDENCE_DIR ??
".state/research/schedulej-joint-zero-oct6/packets";
const xsd = process.env.SCHEDULE_J_JOINT_XSD_PATH ??
Expand Down Expand Up @@ -61,7 +67,7 @@ test("joint C/F above full MFJ QBI phase-out retains two zero-limited 8995-A row
const elected = await packet(`zero-${fishingOwner}`, input);
const p = elected.pending;
const q = calculateMixedFishingQbi(p.form8995a, 2025);
assertScheduleJSourceReturn(elected.result.pending);
assertScheduleJSourceReturn(elected.result.pending, replayScheduleJ);
assertEquals(q.profits, [200_000, 400_000]);
assertEquals(q.allocations, [13_596, 16_275]);
assertEquals(q.rows.map((row) => row.lines.line15), [0, 0]);
Expand Down Expand Up @@ -141,7 +147,7 @@ test("full-phase-out QBI requires byte-bound complete owner-specific zero-limit
const pending: any = structuredClone(result.pending);
pending.form8995a.mixed_fishing_qbi_source.schedule_f.qbi_zero_limit_inventory
.sha256 = "0".repeat(64);
assertThrows(() => assertScheduleJSourceReturn(pending));
assertThrows(() => assertScheduleJSourceReturn(pending, replayScheduleJ));
await assertRejects(() => f1040_2025.prepareReturn(pending, filer));
await assertRejects(() => buildPdfBytes(pending, filer, ".pdf-cache"));
});
Loading
Loading