From c3c0d859081cfed56b1a927b78cbc90b88a5d3ae Mon Sep 17 00:00:00 2001 From: Stefano Fiorini Date: Sun, 15 Mar 2026 01:23:47 -0500 Subject: [PATCH] feat: add us-cpa return model and calculations --- skills/us-cpa/src/us_cpa/returns.py | 122 ++++++++++++++++++++++++++++ skills/us-cpa/tests/test_returns.py | 48 +++++++++++ 2 files changed, 170 insertions(+) create mode 100644 skills/us-cpa/src/us_cpa/returns.py create mode 100644 skills/us-cpa/tests/test_returns.py diff --git a/skills/us-cpa/src/us_cpa/returns.py b/skills/us-cpa/src/us_cpa/returns.py new file mode 100644 index 0000000..c952e6a --- /dev/null +++ b/skills/us-cpa/src/us_cpa/returns.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + + +STANDARD_DEDUCTION_2025 = { + "single": 15750.0, + "married_filing_jointly": 31500.0, + "head_of_household": 23625.0, +} + + +TAX_BRACKETS_2025 = { + "single": [ + (11925.0, 0.10), + (48475.0, 0.12), + (103350.0, 0.22), + (197300.0, 0.24), + (250525.0, 0.32), + (626350.0, 0.35), + (float("inf"), 0.37), + ], + "married_filing_jointly": [ + (23850.0, 0.10), + (96950.0, 0.12), + (206700.0, 0.22), + (394600.0, 0.24), + (501050.0, 0.32), + (751600.0, 0.35), + (float("inf"), 0.37), + ], + "head_of_household": [ + (17000.0, 0.10), + (64850.0, 0.12), + (103350.0, 0.22), + (197300.0, 0.24), + (250500.0, 0.32), + (626350.0, 0.35), + (float("inf"), 0.37), + ], +} + + +def _as_float(value: Any) -> float: + if value in (None, ""): + return 0.0 + return float(value) + + +def tax_on_ordinary_income(amount: float, filing_status: str) -> float: + taxable = max(0.0, amount) + brackets = TAX_BRACKETS_2025[filing_status] + lower = 0.0 + tax = 0.0 + for upper, rate in brackets: + if taxable <= lower: + break + portion = min(taxable, upper) - lower + tax += portion * rate + lower = upper + return round(tax, 2) + + +def resolve_required_forms(normalized: dict[str, Any]) -> list[str]: + forms = ["f1040"] + if normalized["income"]["taxableInterest"] > 1500: + forms.append("f1040sb") + if normalized["income"]["businessIncome"] != 0: + forms.extend(["f1040sc", "f1040se", "f1040s1"]) + return forms + + +def normalize_case_facts(facts: dict[str, Any], tax_year: int) -> dict[str, Any]: + filing_status = facts.get("filingStatus", "single") + wages = _as_float(facts.get("wages")) + interest = _as_float(facts.get("taxableInterest")) + business_income = _as_float(facts.get("businessIncome")) + withholding = _as_float(facts.get("federalWithholding")) + + adjusted_gross_income = wages + interest + business_income + standard_deduction = STANDARD_DEDUCTION_2025[filing_status] + taxable_income = max(0.0, adjusted_gross_income - standard_deduction) + income_tax = tax_on_ordinary_income(taxable_income, filing_status) + self_employment_tax = round(max(0.0, business_income) * 0.9235 * 0.153, 2) + total_tax = round(income_tax + self_employment_tax, 2) + total_payments = withholding + refund = round(max(0.0, total_payments - total_tax), 2) + balance_due = round(max(0.0, total_tax - total_payments), 2) + + normalized = { + "taxYear": tax_year, + "taxpayer": { + "fullName": facts.get("taxpayer.fullName", "Unknown Taxpayer"), + }, + "filingStatus": filing_status, + "income": { + "wages": wages, + "taxableInterest": interest, + "businessIncome": business_income, + }, + "payments": { + "federalWithholding": withholding, + }, + "deductions": { + "standardDeduction": standard_deduction, + }, + "taxes": { + "incomeTax": income_tax, + "selfEmploymentTax": self_employment_tax, + "totalTax": total_tax, + }, + "totals": { + "adjustedGrossIncome": round(adjusted_gross_income, 2), + "taxableIncome": round(taxable_income, 2), + "totalPayments": round(total_payments, 2), + "refund": refund, + "balanceDue": balance_due, + }, + } + normalized["requiredForms"] = resolve_required_forms(normalized) + return normalized diff --git a/skills/us-cpa/tests/test_returns.py b/skills/us-cpa/tests/test_returns.py new file mode 100644 index 0000000..2541e2c --- /dev/null +++ b/skills/us-cpa/tests/test_returns.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +import unittest + +from us_cpa.returns import normalize_case_facts, resolve_required_forms, tax_on_ordinary_income + + +class ReturnModelTests(unittest.TestCase): + def test_normalize_case_facts_computes_basic_1040_totals(self) -> None: + normalized = normalize_case_facts( + { + "taxpayer.fullName": "Jane Doe", + "filingStatus": "single", + "wages": 50000, + "taxableInterest": 100, + "federalWithholding": 6000, + }, + 2025, + ) + + self.assertEqual(normalized["requiredForms"], ["f1040"]) + self.assertEqual(normalized["deductions"]["standardDeduction"], 15750.0) + self.assertEqual(normalized["totals"]["adjustedGrossIncome"], 50100.0) + self.assertEqual(normalized["totals"]["taxableIncome"], 34350.0) + self.assertEqual(normalized["totals"]["refund"], 2116.5) + + def test_resolve_required_forms_adds_business_and_interest_forms(self) -> None: + normalized = normalize_case_facts( + { + "filingStatus": "single", + "wages": 0, + "taxableInterest": 2000, + "businessIncome": 12000, + }, + 2025, + ) + + self.assertEqual( + resolve_required_forms(normalized), + ["f1040", "f1040sb", "f1040sc", "f1040se", "f1040s1"], + ) + + def test_tax_bracket_calculation_uses_2025_single_rates(self) -> None: + self.assertEqual(tax_on_ordinary_income(34350.0, "single"), 3883.5) + + +if __name__ == "__main__": + unittest.main()