Customer 360: extended Client fields, auto-migrate, updated Rolodex CRUD/templates, QDRO routes/views, importer mapping
QDRO links appear in rolodex_view.html case rows and case.html header when QDRO data exists, matching legacy flows.
This commit is contained in:
149
app/main.py
149
app/main.py
@@ -29,7 +29,7 @@ import structlog
|
||||
from structlog import contextvars as structlog_contextvars
|
||||
|
||||
from .database import create_tables, get_db, get_database_url
|
||||
from .models import User, Case, Client, Phone, Transaction, Document, Payment, ImportLog
|
||||
from .models import User, Case, Client, Phone, Transaction, Document, Payment, ImportLog, Qdros
|
||||
from .auth import authenticate_user, get_current_user_from_session
|
||||
from .reporting import (
|
||||
build_phone_book_pdf,
|
||||
@@ -633,16 +633,37 @@ def import_rolodex_data(db: Session, file_path: str) -> Dict[str, Any]:
|
||||
result['errors'].append(f"Row {row_num}: Client with ID '{rolodex_id}' already exists")
|
||||
continue
|
||||
|
||||
# Parse DOB (YYYY-MM-DD or MM/DD/YY variants)
|
||||
dob_raw = row.get('DOB', '').strip()
|
||||
dob_val = None
|
||||
for fmt in ("%Y-%m-%d", "%m/%d/%Y", "%m/%d/%y"):
|
||||
if not dob_raw:
|
||||
break
|
||||
try:
|
||||
dob_val = datetime.strptime(dob_raw, fmt).date()
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
client = Client(
|
||||
rolodex_id=rolodex_id,
|
||||
prefix=row.get('Prefix', '').strip() or None,
|
||||
first_name=row.get('First', '').strip() or None,
|
||||
middle_initial=row.get('Middle', '').strip() or None,
|
||||
middle_name=row.get('Middle', '').strip() or None,
|
||||
last_name=row.get('Last', '').strip() or None,
|
||||
suffix=row.get('Suffix', '').strip() or None,
|
||||
title=row.get('Title', '').strip() or None,
|
||||
company=row.get('Title', '').strip() or None,
|
||||
address=row.get('A1', '').strip() or None,
|
||||
city=row.get('City', '').strip() or None,
|
||||
state=row.get('St', '').strip() or None,
|
||||
zip_code=row.get('Zip', '').strip() or None
|
||||
state=(row.get('Abrev', '').strip() or row.get('St', '').strip() or None),
|
||||
zip_code=row.get('Zip', '').strip() or None,
|
||||
group=row.get('Group', '').strip() or None,
|
||||
email=row.get('Email', '').strip() or None,
|
||||
dob=dob_val,
|
||||
ssn=row.get('SS#', '').strip() or None,
|
||||
legal_status=row.get('Legal_Status', '').strip() or None,
|
||||
memo=row.get('Memo', '').strip() or None,
|
||||
)
|
||||
|
||||
db.add(client)
|
||||
@@ -2298,6 +2319,9 @@ async def case_detail(
|
||||
|
||||
logger.info("case_detail", case_id=case_obj.id, file_no=case_obj.file_no)
|
||||
|
||||
# Determine if QDRO entries exist for this case's file number
|
||||
has_qdro = db.query(Qdros).filter(Qdros.file_no == case_obj.file_no).count() > 0
|
||||
|
||||
# Get any errors from session and clear them
|
||||
errors = request.session.pop("case_update_errors", None)
|
||||
|
||||
@@ -2322,6 +2346,7 @@ async def case_detail(
|
||||
"saved": saved,
|
||||
"errors": errors or [],
|
||||
"totals": totals,
|
||||
"has_qdro": has_qdro,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -2658,13 +2683,23 @@ async def rolodex_view(client_id: int, request: Request, db: Session = Depends(g
|
||||
@app.post("/rolodex/create")
|
||||
async def rolodex_create(
|
||||
request: Request,
|
||||
prefix: str = Form(None),
|
||||
first_name: str = Form(None),
|
||||
middle_name: str = Form(None),
|
||||
last_name: str = Form(None),
|
||||
suffix: str = Form(None),
|
||||
title: str = Form(None),
|
||||
company: str = Form(None),
|
||||
address: str = Form(None),
|
||||
city: str = Form(None),
|
||||
state: str = Form(None),
|
||||
zip_code: str = Form(None),
|
||||
group: str = Form(None),
|
||||
email: str = Form(None),
|
||||
dob: str = Form(None),
|
||||
ssn: str = Form(None),
|
||||
legal_status: str = Form(None),
|
||||
memo: str = Form(None),
|
||||
rolodex_id: str = Form(None),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -2672,14 +2707,32 @@ async def rolodex_create(
|
||||
if not user:
|
||||
return RedirectResponse(url="/login", status_code=302)
|
||||
|
||||
# Parse date
|
||||
dob_dt = None
|
||||
if dob and dob.strip():
|
||||
try:
|
||||
dob_dt = datetime.strptime(dob.strip(), "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
dob_dt = None
|
||||
|
||||
client = Client(
|
||||
prefix=(prefix or "").strip() or None,
|
||||
first_name=(first_name or "").strip() or None,
|
||||
middle_name=(middle_name or "").strip() or None,
|
||||
last_name=(last_name or "").strip() or None,
|
||||
suffix=(suffix or "").strip() or None,
|
||||
title=(title or "").strip() or None,
|
||||
company=(company or "").strip() or None,
|
||||
address=(address or "").strip() or None,
|
||||
city=(city or "").strip() or None,
|
||||
state=(state or "").strip() or None,
|
||||
zip_code=(zip_code or "").strip() or None,
|
||||
group=(group or "").strip() or None,
|
||||
email=(email or "").strip() or None,
|
||||
dob=dob_dt,
|
||||
ssn=(ssn or "").strip() or None,
|
||||
legal_status=(legal_status or "").strip() or None,
|
||||
memo=(memo or "").strip() or None,
|
||||
rolodex_id=(rolodex_id or "").strip() or None,
|
||||
)
|
||||
db.add(client)
|
||||
@@ -2693,13 +2746,23 @@ async def rolodex_create(
|
||||
async def rolodex_update(
|
||||
client_id: int,
|
||||
request: Request,
|
||||
prefix: str = Form(None),
|
||||
first_name: str = Form(None),
|
||||
middle_name: str = Form(None),
|
||||
last_name: str = Form(None),
|
||||
suffix: str = Form(None),
|
||||
title: str = Form(None),
|
||||
company: str = Form(None),
|
||||
address: str = Form(None),
|
||||
city: str = Form(None),
|
||||
state: str = Form(None),
|
||||
zip_code: str = Form(None),
|
||||
group: str = Form(None),
|
||||
email: str = Form(None),
|
||||
dob: str = Form(None),
|
||||
ssn: str = Form(None),
|
||||
legal_status: str = Form(None),
|
||||
memo: str = Form(None),
|
||||
rolodex_id: str = Form(None),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -2711,13 +2774,27 @@ async def rolodex_update(
|
||||
if not client:
|
||||
raise HTTPException(status_code=404, detail="Client not found")
|
||||
|
||||
client.prefix = (prefix or "").strip() or None
|
||||
client.first_name = (first_name or "").strip() or None
|
||||
client.middle_name = (middle_name or "").strip() or None
|
||||
client.last_name = (last_name or "").strip() or None
|
||||
client.suffix = (suffix or "").strip() or None
|
||||
client.title = (title or "").strip() or None
|
||||
client.company = (company or "").strip() or None
|
||||
client.address = (address or "").strip() or None
|
||||
client.city = (city or "").strip() or None
|
||||
client.state = (state or "").strip() or None
|
||||
client.zip_code = (zip_code or "").strip() or None
|
||||
client.group = (group or "").strip() or None
|
||||
client.email = (email or "").strip() or None
|
||||
if dob and dob.strip():
|
||||
try:
|
||||
client.dob = datetime.strptime(dob.strip(), "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
pass
|
||||
client.ssn = (ssn or "").strip() or None
|
||||
client.legal_status = (legal_status or "").strip() or None
|
||||
client.memo = (memo or "").strip() or None
|
||||
client.rolodex_id = (rolodex_id or "").strip() or None
|
||||
|
||||
db.commit()
|
||||
@@ -3625,3 +3702,67 @@ async def api_list_ledger(
|
||||
items=items,
|
||||
pagination=Pagination(page=page, page_size=page_size, total=total, total_pages=total_pages),
|
||||
)
|
||||
|
||||
# ------------------------------
|
||||
# QDRO Views
|
||||
# ------------------------------
|
||||
|
||||
@app.get("/qdro/{file_no}")
|
||||
async def qdro_versions(
|
||||
request: Request,
|
||||
file_no: str,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
user = get_current_user_from_session(request.session)
|
||||
if not user:
|
||||
return RedirectResponse(url="/login", status_code=302)
|
||||
|
||||
versions = (
|
||||
db.query(Qdros)
|
||||
.filter(Qdros.file_no == file_no)
|
||||
.order_by(Qdros.version.asc())
|
||||
.all()
|
||||
)
|
||||
return templates.TemplateResponse(
|
||||
"qdro.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"file_no": file_no,
|
||||
"versions": versions,
|
||||
"qdro": None,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/qdro/{file_no}/{version}")
|
||||
async def qdro_detail(
|
||||
request: Request,
|
||||
file_no: str,
|
||||
version: str,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
user = get_current_user_from_session(request.session)
|
||||
if not user:
|
||||
return RedirectResponse(url="/login", status_code=302)
|
||||
|
||||
q = db.query(Qdros).filter(Qdros.file_no == file_no, Qdros.version == version).first()
|
||||
if not q:
|
||||
return RedirectResponse(url=f"/qdro/{file_no}", status_code=302)
|
||||
|
||||
versions = (
|
||||
db.query(Qdros)
|
||||
.filter(Qdros.file_no == file_no)
|
||||
.order_by(Qdros.version.asc())
|
||||
.all()
|
||||
)
|
||||
return templates.TemplateResponse(
|
||||
"qdro.html",
|
||||
{
|
||||
"request": request,
|
||||
"user": user,
|
||||
"file_no": file_no,
|
||||
"versions": versions,
|
||||
"qdro": q,
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user