feat: add us-cpa preparation workflow
This commit is contained in:
@@ -7,6 +7,7 @@ from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from us_cpa.cases import CaseConflictError, CaseManager
|
||||
from us_cpa.prepare import EfileExporter, PrepareEngine, render_case_forms
|
||||
from us_cpa.questions import QuestionEngine, render_analysis, render_memo
|
||||
from us_cpa.sources import TaxYearCorpus, bootstrap_irs_catalog
|
||||
|
||||
@@ -167,7 +168,40 @@ def main(argv: list[str] | None = None) -> int:
|
||||
}
|
||||
return _emit(payload, args.format)
|
||||
|
||||
if args.command in {"prepare", "review", "render-forms", "export-efile-ready"}:
|
||||
if args.command == "prepare":
|
||||
case_dir = _require_case_dir(args)
|
||||
payload = {
|
||||
"command": args.command,
|
||||
"format": args.format,
|
||||
**PrepareEngine().prepare_case(case_dir),
|
||||
}
|
||||
return _emit(payload, args.format)
|
||||
|
||||
if args.command == "render-forms":
|
||||
case_dir = _require_case_dir(args)
|
||||
manager = CaseManager(case_dir)
|
||||
manifest = manager.load_manifest()
|
||||
normalized = json.loads((case_dir / "return" / "normalized-return.json").read_text())
|
||||
artifacts = render_case_forms(case_dir, TaxYearCorpus(), normalized)
|
||||
payload = {
|
||||
"command": "render-forms",
|
||||
"format": args.format,
|
||||
"taxYear": manifest["taxYear"],
|
||||
"status": "rendered",
|
||||
**artifacts,
|
||||
}
|
||||
return _emit(payload, args.format)
|
||||
|
||||
if args.command == "export-efile-ready":
|
||||
case_dir = _require_case_dir(args)
|
||||
payload = {
|
||||
"command": "export-efile-ready",
|
||||
"format": args.format,
|
||||
**EfileExporter().export_case(case_dir),
|
||||
}
|
||||
return _emit(payload, args.format)
|
||||
|
||||
if args.command == "review":
|
||||
case_dir = _require_case_dir(args)
|
||||
payload = {
|
||||
"command": args.command,
|
||||
|
||||
75
skills/us-cpa/src/us_cpa/prepare.py
Normal file
75
skills/us-cpa/src/us_cpa/prepare.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from us_cpa.cases import CaseManager
|
||||
from us_cpa.renderers import render_case_forms
|
||||
from us_cpa.returns import normalize_case_facts
|
||||
from us_cpa.sources import TaxYearCorpus
|
||||
|
||||
|
||||
def _load_case_facts(case_dir: Path) -> dict[str, Any]:
|
||||
facts_path = case_dir / "extracted" / "facts.json"
|
||||
payload = json.loads(facts_path.read_text())
|
||||
return {key: value["value"] for key, value in payload["facts"].items()}
|
||||
|
||||
|
||||
|
||||
class PrepareEngine:
|
||||
def __init__(self, *, corpus: TaxYearCorpus | None = None) -> None:
|
||||
self.corpus = corpus or TaxYearCorpus()
|
||||
|
||||
def prepare_case(self, case_dir: Path) -> dict[str, Any]:
|
||||
manager = CaseManager(case_dir)
|
||||
manifest = manager.load_manifest()
|
||||
facts = _load_case_facts(manager.case_dir)
|
||||
normalized = normalize_case_facts(facts, manifest["taxYear"])
|
||||
normalized_path = manager.case_dir / "return" / "normalized-return.json"
|
||||
normalized_path.write_text(json.dumps(normalized, indent=2))
|
||||
|
||||
artifacts = render_case_forms(manager.case_dir, self.corpus, normalized)
|
||||
unresolved_issues = json.loads(manager.issues_path.read_text())["issues"]
|
||||
|
||||
summary = {
|
||||
"requiredForms": normalized["requiredForms"],
|
||||
"reviewRequiredArtifacts": [
|
||||
artifact["formCode"] for artifact in artifacts["artifacts"] if artifact["reviewRequired"]
|
||||
],
|
||||
"refund": normalized["totals"]["refund"],
|
||||
"balanceDue": normalized["totals"]["balanceDue"],
|
||||
"unresolvedIssueCount": len(unresolved_issues),
|
||||
}
|
||||
result = {
|
||||
"status": "prepared",
|
||||
"caseDir": str(manager.case_dir),
|
||||
"taxYear": manifest["taxYear"],
|
||||
"normalizedReturnPath": str(normalized_path),
|
||||
"artifactManifestPath": str(manager.case_dir / "output" / "artifacts.json"),
|
||||
"summary": summary,
|
||||
}
|
||||
(manager.case_dir / "reports" / "prepare-summary.json").write_text(json.dumps(result, indent=2))
|
||||
return result
|
||||
|
||||
|
||||
class EfileExporter:
|
||||
def export_case(self, case_dir: Path) -> dict[str, Any]:
|
||||
case_dir = Path(case_dir).expanduser().resolve()
|
||||
normalized = json.loads((case_dir / "return" / "normalized-return.json").read_text())
|
||||
artifacts = json.loads((case_dir / "output" / "artifacts.json").read_text())
|
||||
issues = json.loads((case_dir / "issues" / "open-issues.json").read_text())["issues"]
|
||||
payload = {
|
||||
"status": "draft" if issues or any(a["reviewRequired"] for a in artifacts["artifacts"]) else "ready",
|
||||
"taxYear": normalized["taxYear"],
|
||||
"returnSummary": {
|
||||
"requiredForms": normalized["requiredForms"],
|
||||
"refund": normalized["totals"]["refund"],
|
||||
"balanceDue": normalized["totals"]["balanceDue"],
|
||||
},
|
||||
"attachments": artifacts["artifacts"],
|
||||
"unresolvedIssues": issues,
|
||||
}
|
||||
output_path = case_dir / "output" / "efile-ready.json"
|
||||
output_path.write_text(json.dumps(payload, indent=2))
|
||||
return payload
|
||||
@@ -14,9 +14,9 @@ TOPIC_RULES = [
|
||||
"keywords": ("standard deduction",),
|
||||
"authority_slugs": ("i1040gi",),
|
||||
"answer_by_status": {
|
||||
"single": "$15,000",
|
||||
"married_filing_jointly": "$30,000",
|
||||
"head_of_household": "$22,500",
|
||||
"single": "$15,750",
|
||||
"married_filing_jointly": "$31,500",
|
||||
"head_of_household": "$23,625",
|
||||
},
|
||||
"summary_template": "{filing_status_label} filers use a {answer} standard deduction for tax year {tax_year}.",
|
||||
"confidence": "high",
|
||||
|
||||
Reference in New Issue
Block a user