finishing QDRO section

This commit is contained in:
HotSwapp
2025-08-15 17:19:51 -05:00
parent 006ef3d7b1
commit abc7f289d1
22 changed files with 2753 additions and 46 deletions

View File

@@ -6,7 +6,7 @@ from .user import User
from .rolodex import Rolodex, Phone
from .files import File
from .ledger import Ledger
from .qdro import QDRO
from .qdro import QDRO, QDROVersion, QDROCommunication
from .audit import AuditLog, LoginAttempt, ImportAudit, ImportAuditFile
from .auth import RefreshToken
from .additional import Deposit, Payment, FileNote, FormVariable, ReportVariable, Document
@@ -16,6 +16,7 @@ from .pensions import (
Pension, PensionSchedule, MarriageHistory, DeathBenefit,
SeparationAgreement, LifeTable, NumberTable, PensionResult
)
from .templates import DocumentTemplate, DocumentTemplateVersion, TemplateKeyword
from .lookups import (
Employee, FileType, FileStatus, TransactionType, TransactionCode,
State, GroupLookup, Footer, PlanInfo, FormIndex, FormList,
@@ -23,7 +24,7 @@ from .lookups import (
)
__all__ = [
"BaseModel", "User", "Rolodex", "Phone", "File", "Ledger", "QDRO",
"BaseModel", "User", "Rolodex", "Phone", "File", "Ledger", "QDRO", "QDROVersion", "QDROCommunication",
"AuditLog", "LoginAttempt", "ImportAudit", "ImportAuditFile", "RefreshToken",
"Deposit", "Payment", "FileNote", "FormVariable", "ReportVariable", "Document", "FlexibleImport",
"SupportTicket", "TicketResponse", "TicketStatus", "TicketPriority", "TicketCategory",
@@ -31,5 +32,5 @@ __all__ = [
"SeparationAgreement", "LifeTable", "NumberTable", "PensionResult",
"Employee", "FileType", "FileStatus", "TransactionType", "TransactionCode",
"State", "GroupLookup", "Footer", "PlanInfo", "FormIndex", "FormList",
"PrinterSetup", "SystemSetup", "FormKeyword"
"PrinterSetup", "SystemSetup", "FormKeyword", "TemplateKeyword"
]

View File

@@ -1,8 +1,9 @@
"""
QDRO models based on legacy QDRO.SC analysis
"""
from sqlalchemy import Column, Integer, String, Date, Text, ForeignKey
from sqlalchemy import Column, Integer, String, Date, Text, ForeignKey, DateTime
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.models.base import BaseModel
@@ -15,8 +16,11 @@ class QDRO(BaseModel):
id = Column(Integer, primary_key=True, autoincrement=True)
file_no = Column(String(45), ForeignKey("files.file_no"), nullable=False)
version = Column(String(10), default="01") # Version of QDRO
plan_id = Column(String(45)) # Plan identifier
version = Column(String(10), default="01") # Version of QDRO (current working version)
plan_id = Column(String(45)) # Plan identifier (links to PlanInfo.plan_id)
# Timestamps (explicit created_at for sort consistency across APIs)
created_at = Column(DateTime(timezone=True), server_default=func.now())
# CSV fields from legacy system
field1 = Column(String(100)) # ^1 field
@@ -55,12 +59,61 @@ class QDRO(BaseModel):
form_name = Column(String(200)) # Form/template name
# Additional fields
status = Column(String(45), default="DRAFT") # DRAFT, APPROVED, FILED, etc.
status = Column(String(45), default="DRAFT") # DRAFT, APPROVAL_PENDING, APPROVED, FILED, etc.
content = Column(Text) # Document content/template
notes = Column(Text) # Additional notes
# Court/cycle tracking (idempotent schema updater will add when missing)
approval_status = Column(String(45)) # Workflow status if different granularity is needed
approved_date = Column(Date)
filed_date = Column(Date)
# Relationships
file = relationship("File", back_populates="qdros")
versions = relationship("QDROVersion", back_populates="qdro", cascade="all, delete-orphan")
communications = relationship("QDROCommunication", back_populates="qdro", cascade="all, delete-orphan")
def __repr__(self):
return f"<QDRO(file_no='{self.file_no}', version='{self.version}', case_number='{self.case_number}')>"
return f"<QDRO(file_no='{self.file_no}', version='{self.version}', case_number='{self.case_number}')>"
class QDROVersion(BaseModel):
"""
Immutable snapshot of a QDRO at a point in time for version tracking.
"""
__tablename__ = "qdro_versions"
id = Column(Integer, primary_key=True, autoincrement=True)
qdro_id = Column(Integer, ForeignKey("qdros.id"), nullable=False, index=True)
version_label = Column(String(20), nullable=False, default="01")
status = Column(String(45), default="DRAFT")
content = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())
qdro = relationship("QDRO", back_populates="versions")
def __repr__(self):
return f"<QDROVersion(qdro_id={self.qdro_id}, version='{self.version_label}', status='{self.status}')>"
class QDROCommunication(BaseModel):
"""
Track communications with plan administrators or other parties regarding a QDRO.
"""
__tablename__ = "qdro_communications"
id = Column(Integer, primary_key=True, autoincrement=True)
qdro_id = Column(Integer, ForeignKey("qdros.id"), nullable=False, index=True)
channel = Column(String(20)) # email | phone | letter | fax | portal
subject = Column(String(200))
message = Column(Text)
contact_name = Column(String(100))
contact_email = Column(String(200))
contact_phone = Column(String(50))
status = Column(String(45)) # sent | received | pending | escalated
created_at = Column(DateTime(timezone=True), server_default=func.now())
qdro = relationship("QDRO", back_populates="communications")
def __repr__(self):
return f"<QDROCommunication(qdro_id={self.qdro_id}, channel='{self.channel}', subject='{self.subject}')>"

98
app/models/templates.py Normal file
View File

@@ -0,0 +1,98 @@
"""
Document Template and Version models
"""
from sqlalchemy import Column, Integer, String, Text, ForeignKey, Boolean, UniqueConstraint
from sqlalchemy.orm import relationship
from app.models.base import BaseModel
class DocumentTemplate(BaseModel):
"""
High-level template metadata and current version pointer.
"""
__tablename__ = "document_templates"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
name = Column(String(200), unique=True, nullable=False, index=True)
description = Column(Text)
category = Column(String(100), index=True, default="GENERAL")
active = Column(Boolean, default=True, nullable=False)
# Creator username (optional)
created_by = Column(String(150), ForeignKey("users.username"), nullable=True)
# Pointer to the currently-approved version
current_version_id = Column(Integer, ForeignKey("document_template_versions.id", use_alter=True), nullable=True)
# Relationships
current_version = relationship(
"DocumentTemplateVersion",
foreign_keys=[current_version_id],
post_update=True,
primaryjoin="DocumentTemplate.current_version_id==DocumentTemplateVersion.id",
uselist=False,
)
versions = relationship(
"DocumentTemplateVersion",
back_populates="template",
cascade="all, delete-orphan",
order_by="desc(DocumentTemplateVersion.created_at)",
foreign_keys="DocumentTemplateVersion.template_id",
)
keywords = relationship(
"TemplateKeyword",
back_populates="template",
cascade="all, delete-orphan",
order_by="asc(TemplateKeyword.keyword)",
foreign_keys="TemplateKeyword.template_id",
)
class DocumentTemplateVersion(BaseModel):
"""
Template binary version metadata and storage location
"""
__tablename__ = "document_template_versions"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
template_id = Column(Integer, ForeignKey("document_templates.id"), nullable=False, index=True)
semantic_version = Column(String(50), nullable=False, index=True) # e.g., 1.0.0
storage_path = Column(String(512), nullable=False) # local path or S3 URI
mime_type = Column(String(100), nullable=False)
size = Column(Integer, nullable=False, default=0)
checksum = Column(String(64), nullable=False) # sha256 hex
changelog = Column(Text)
created_by = Column(String(150), ForeignKey("users.username"), nullable=True)
is_approved = Column(Boolean, default=True, nullable=False)
# Relationships
template = relationship(
"DocumentTemplate",
back_populates="versions",
foreign_keys=[template_id],
primaryjoin="DocumentTemplateVersion.template_id==DocumentTemplate.id",
)
class TemplateKeyword(BaseModel):
"""
Keyword/tag assigned to a DocumentTemplate.
"""
__tablename__ = "template_keywords"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
template_id = Column(Integer, ForeignKey("document_templates.id"), nullable=False, index=True)
keyword = Column(String(100), nullable=False, index=True)
__table_args__ = (
UniqueConstraint("template_id", "keyword", name="uq_template_keyword"),
)
template = relationship(
"DocumentTemplate",
back_populates="keywords",
foreign_keys=[template_id],
primaryjoin="TemplateKeyword.template_id==DocumentTemplate.id",
)

View File

@@ -24,6 +24,7 @@ class User(BaseModel):
# Authorization
is_active = Column(Boolean, default=True)
is_admin = Column(Boolean, default=False)
is_approver = Column(Boolean, default=False)
# User Preferences
theme_preference = Column(String(10), default='light') # 'light', 'dark'