utility-app/tools/doc_generator/logic/calculations/legacy_legal.py

136 lines
3.8 KiB
Python

from __future__ import annotations
from datetime import datetime
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
import pendulum
def clean_money(value) -> Decimal:
text = str(value or "").replace("$", "").replace(",", "").strip()
if not text:
return Decimal("0")
try:
return Decimal(text)
except InvalidOperation:
return Decimal("0")
def clean_int(value) -> int:
try:
return max(0, int(str(value or "0").strip()))
except ValueError:
return 0
def money(value: Decimal) -> str:
value = value.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
return f"${value:,.2f}"
def parse_date(value):
text = str(value or "").strip()
if not text:
return None
for fmt in ("YYYY-MM-DD", "MM/DD/YYYY", "MM-DD-YYYY"):
try:
return pendulum.from_format(text, fmt)
except Exception:
pass
try:
return pendulum.parse(text)
except Exception:
return None
def format_date(value) -> str:
if not value:
return ""
return value.format("MMMM Do, YYYY")
def add_months(date_value, months: int):
if not date_value:
return None
return date_value.add(months=months)
def add_last_four_fields(data: dict) -> dict:
# Existing old-template compatibility.
for key, value in list(data.items()):
if not key.endswith("Account"):
continue
base = key[:-len("Account")]
digits = "".join(ch for ch in str(value or "") if ch.isdigit())
data[f"{base}AccLastFour"] = digits[-4:] if len(digits) >= 4 else digits
ssn = "".join(ch for ch in str(data.get("SSN", "")) if ch.isdigit())
if ssn:
data["SSNLastFour"] = ssn[-4:]
ssn2 = "".join(ch for ch in str(data.get("client2SSN", "")) if ch.isdigit())
if ssn2:
data["SSN2LastFour"] = ssn2[-4:]
return data
def add_case_filename_fields(data: dict) -> dict:
plaintiff = str(data.get("casePlaintiff", "") or "")
data["casePlaintiffFileName"] = plaintiff.replace(" ", "-").replace(",", "").replace(".", "")
return data
def add_settlement_schedule(data: dict, calculation: dict) -> dict:
config = (calculation.get("outputsDynamic") or {}).get("settlementSchedule", {})
count_field = config.get("countField", "settlementInstallmentNo")
count = clean_int(data.get(count_field))
# Safety cap. Old app used many fixed fields; 120 is plenty for MVP.
count = min(count, int(config.get("maxCount", 120)))
settlement_amount = clean_money(data.get("settlementAmount"))
installment_amount = clean_money(data.get("settlementInstallmentAmount"))
first_payment_date = parse_date(data.get("settlementFirstPaymentDate"))
if count <= 0:
return data
if installment_amount == 0 and settlement_amount > 0:
installment_amount = (settlement_amount / Decimal(count)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
remaining = settlement_amount
for i in range(count):
suffix = f"{i:02d}"
payment_date = add_months(first_payment_date, i)
payment = installment_amount
if settlement_amount > 0:
if i == count - 1:
payment = remaining
remaining = max(Decimal("0"), remaining - payment)
data[f"settlementPaymentDate{suffix}"] = format_date(payment_date)
data[f"settlementPaymentAmount{suffix}"] = money(payment)
data[f"settlementRemaingBalance{suffix}"] = money(remaining)
# Correct spelling alias for future templates.
data[f"settlementRemainingBalance{suffix}"] = money(remaining)
return data
def apply_legacy_legal_calculations(data: dict, calculation: dict) -> dict:
data = dict(data)
data = add_last_four_fields(data)
data = add_case_filename_fields(data)
data = add_settlement_schedule(data, calculation)
return data