136 lines
3.8 KiB
Python
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
|