feat: add us-cpa return model and calculations
This commit is contained in:
122
skills/us-cpa/src/us_cpa/returns.py
Normal file
122
skills/us-cpa/src/us_cpa/returns.py
Normal file
@@ -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
|
||||
48
skills/us-cpa/tests/test_returns.py
Normal file
48
skills/us-cpa/tests/test_returns.py
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user