Files
delphi-database-v2/app/models.py
HotSwapp 216adcc1f6 feat: Implement comprehensive admin panel with CSV import system
- Add ImportLog model for tracking import history and results
- Create admin.html template with file upload form and progress display
- Implement POST /admin/upload route for CSV file handling with validation
- Build CSV import engine with dispatcher routing by filename patterns:
  * ROLODEX*.csv → Client model import
  * PHONE*.csv → Phone model import with client linking
  * FILES*.csv → Case model import
  * LEDGER*.csv → Transaction model import
  * QDROS*.csv → Document model import
  * PAYMENTS*.csv → Payment model import
- Add POST /admin/import/{data_type} route for triggering imports
- Implement comprehensive validation, error handling, and progress tracking
- Support for CSV header validation, data type conversions, and duplicate handling
- Real-time progress tracking with ImportLog database model
- Responsive UI with Bootstrap components for upload and results display
- Enhanced navigation with admin panel link already in place
- Tested import functionality with validation and error handling

The admin panel enables bulk importing of legacy CSV data from the old-csv/ directory, making the system fully functional with real data.
2025-10-06 19:52:31 -05:00

211 lines
7.0 KiB
Python

"""
SQLAlchemy models for the Delphi database.
All models inherit from Base which is configured in the database module.
"""
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Float, Text, Boolean
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func
# Create Base for SQLAlchemy 1.x compatibility
Base = declarative_base()
class User(Base):
"""
User model for authentication.
Stores user credentials and basic information for login functionality.
"""
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, index=True, nullable=False)
password_hash = Column(String(255), nullable=False)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
def __repr__(self):
return f"<User(id={self.id}, username='{self.username}')>"
class Client(Base):
"""
Client model representing individuals or entities.
Core client information imported from ROLODEX data.
"""
__tablename__ = "clients"
id = Column(Integer, primary_key=True, index=True)
rolodex_id = Column(String(20), unique=True, index=True)
last_name = Column(String(50))
first_name = Column(String(50))
middle_initial = Column(String(10))
company = Column(String(100))
address = Column(String(255))
city = Column(String(50))
state = Column(String(2))
zip_code = Column(String(10))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
phones = relationship("Phone", back_populates="client")
cases = relationship("Case", back_populates="client")
def __repr__(self):
return f"<Client(id={self.id}, name='{self.first_name} {self.last_name}')>"
class Phone(Base):
"""
Phone number model linked to clients.
Stores phone number information for clients.
"""
__tablename__ = "phones"
id = Column(Integer, primary_key=True, index=True)
client_id = Column(Integer, ForeignKey("clients.id"), nullable=False)
phone_type = Column(String(20)) # home, work, mobile, fax, etc.
phone_number = Column(String(20))
extension = Column(String(10))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
client = relationship("Client", back_populates="phones")
def __repr__(self):
return f"<Phone(id={self.id}, number='{self.phone_number}')>"
class Case(Base):
"""
Case model representing legal cases or files.
Main case information imported from FILES data.
"""
__tablename__ = "cases"
id = Column(Integer, primary_key=True, index=True)
file_no = Column(String(20), unique=True, index=True, nullable=False)
client_id = Column(Integer, ForeignKey("clients.id"), nullable=False)
status = Column(String(20), default="active")
case_type = Column(String(50))
description = Column(Text)
open_date = Column(DateTime(timezone=True))
close_date = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
client = relationship("Client", back_populates="cases")
transactions = relationship("Transaction", back_populates="case")
documents = relationship("Document", back_populates="case")
payments = relationship("Payment", back_populates="case")
def __repr__(self):
return f"<Case(id={self.id}, file_no='{self.file_no}')>"
class Transaction(Base):
"""
Transaction model for financial transactions.
Records financial activities related to cases.
"""
__tablename__ = "transactions"
id = Column(Integer, primary_key=True, index=True)
case_id = Column(Integer, ForeignKey("cases.id"), nullable=False)
transaction_date = Column(DateTime(timezone=True))
transaction_type = Column(String(20))
amount = Column(Float)
description = Column(Text)
reference = Column(String(50))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
case = relationship("Case", back_populates="transactions")
def __repr__(self):
return f"<Transaction(id={self.id}, amount={self.amount})>"
class Document(Base):
"""
Document model for case-related documents.
Stores information about documents associated with cases.
"""
__tablename__ = "documents"
id = Column(Integer, primary_key=True, index=True)
case_id = Column(Integer, ForeignKey("cases.id"), nullable=False)
document_type = Column(String(50))
file_name = Column(String(255))
file_path = Column(String(500))
description = Column(Text)
uploaded_date = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
case = relationship("Case", back_populates="documents")
def __repr__(self):
return f"<Document(id={self.id}, file_name='{self.file_name}')>"
class Payment(Base):
"""
Payment model for payment records.
Records payments made or received for cases.
"""
__tablename__ = "payments"
id = Column(Integer, primary_key=True, index=True)
case_id = Column(Integer, ForeignKey("cases.id"), nullable=False)
payment_date = Column(DateTime(timezone=True))
payment_type = Column(String(20))
amount = Column(Float)
description = Column(Text)
check_number = Column(String(20))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
case = relationship("Case", back_populates="payments")
def __repr__(self):
return f"<Payment(id={self.id}, amount={self.amount})>"
class ImportLog(Base):
"""
ImportLog model for tracking CSV import operations.
Records the history and results of bulk data imports from legacy CSV files.
"""
__tablename__ = "import_logs"
id = Column(Integer, primary_key=True, index=True)
import_type = Column(String(50), nullable=False) # client, phone, case, transaction, document, payment
file_name = Column(String(255), nullable=False)
file_path = Column(String(500), nullable=False)
status = Column(String(20), default="pending") # pending, running, completed, failed
total_rows = Column(Integer, default=0)
processed_rows = Column(Integer, default=0)
success_count = Column(Integer, default=0)
error_count = Column(Integer, default=0)
error_details = Column(Text) # JSON string of error details
started_at = Column(DateTime(timezone=True), server_default=func.now())
completed_at = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
def __repr__(self):
return f"<ImportLog(id={self.id}, type='{self.import_type}', status='{self.status}')>"