This commit is contained in:
HotSwapp
2025-08-18 20:20:04 -05:00
parent 89b2bc0aa2
commit bac8cc4bd5
114 changed files with 30258 additions and 1341 deletions

View File

@@ -16,6 +16,7 @@ from app.models.user import User
from app.auth.security import get_current_user
from app.services.cache import invalidate_search_cache
from app.services.query_utils import apply_sorting, paginate_with_total
from app.models.additional import Deposit, Payment
router = APIRouter()
@@ -81,6 +82,23 @@ class PaginatedLedgerResponse(BaseModel):
total: int
class DepositResponse(BaseModel):
deposit_date: date
total: float
notes: Optional[str] = None
payments: Optional[List[Dict]] = None # Optional, depending on include_payments
class PaymentCreate(BaseModel):
file_no: Optional[str] = None
client_id: Optional[str] = None
regarding: Optional[str] = None
amount: float
note: Optional[str] = None
payment_method: str = "CHECK"
reference: Optional[str] = None
apply_to_trust: bool = False
@router.get("/ledger/{file_no}", response_model=Union[List[LedgerResponse], PaginatedLedgerResponse])
async def get_file_ledger(
file_no: str,
@@ -324,6 +342,59 @@ async def _update_file_balances(file_obj: File, db: Session):
db.commit()
async def _create_ledger_payment(
file_no: str,
amount: float,
payment_date: date,
payment_method: str,
reference: Optional[str],
notes: Optional[str],
apply_to_trust: bool,
empl_num: str,
db: Session
) -> Ledger:
# Get next item number
max_item = db.query(func.max(Ledger.item_no)).filter(
Ledger.file_no == file_no
).scalar() or 0
# Determine transaction type and code
if apply_to_trust:
t_type = "1" # Trust
t_code = "TRUST"
description = f"Trust deposit - {payment_method}"
else:
t_type = "5" # Credit/Payment
t_code = "PMT"
description = f"Payment received - {payment_method}"
if reference:
description += f" - Ref: {reference}"
if notes:
description += f" - {notes}"
# Create ledger entry
entry = Ledger(
file_no=file_no,
item_no=max_item + 1,
date=payment_date,
t_code=t_code,
t_type=t_type,
t_type_l="C", # Credit
empl_num=empl_num,
quantity=0.0,
rate=0.0,
amount=amount,
billed="Y", # Payments are automatically considered "billed"
note=description
)
db.add(entry)
db.flush() # To get ID
return entry
# Additional Financial Management Endpoints
@router.get("/time-entries/recent")
@@ -819,56 +890,27 @@ async def record_payment(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Record a payment against a file"""
# Verify file exists
file_obj = db.query(File).filter(File.file_no == file_no).first()
if not file_obj:
raise HTTPException(status_code=404, detail="File not found")
payment_date = payment_date or date.today()
# Get next item number
max_item = db.query(func.max(Ledger.item_no)).filter(
Ledger.file_no == file_no
).scalar() or 0
# Determine transaction type and code based on whether it goes to trust
if apply_to_trust:
t_type = "1" # Trust
t_code = "TRUST"
description = f"Trust deposit - {payment_method}"
else:
t_type = "5" # Credit/Payment
t_code = "PMT"
description = f"Payment received - {payment_method}"
if reference:
description += f" - Ref: {reference}"
if notes:
description += f" - {notes}"
# Create payment entry
entry = Ledger(
entry = await _create_ledger_payment(
file_no=file_no,
item_no=max_item + 1,
date=payment_date,
t_code=t_code,
t_type=t_type,
t_type_l="C", # Credit
empl_num=file_obj.empl_num,
quantity=0.0,
rate=0.0,
amount=amount,
billed="Y", # Payments are automatically considered "billed"
note=description
payment_date=payment_date,
payment_method=payment_method,
reference=reference,
notes=notes,
apply_to_trust=apply_to_trust,
empl_num=file_obj.empl_num,
db=db
)
db.add(entry)
db.commit()
db.refresh(entry)
# Update file balances
await _update_file_balances(file_obj, db)
return {
@@ -952,4 +994,157 @@ async def record_expense(
"description": description,
"employee": empl_num
}
}
}
@router.post("/deposits/")
async def create_deposit(
deposit_date: date,
notes: Optional[str] = None,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
existing = db.query(Deposit).filter(Deposit.deposit_date == deposit_date).first()
if existing:
raise HTTPException(status_code=400, detail="Deposit for this date already exists")
deposit = Deposit(
deposit_date=deposit_date,
total=0.0,
notes=notes
)
db.add(deposit)
db.commit()
db.refresh(deposit)
return deposit
@router.post("/deposits/{deposit_date}/payments/")
async def add_payment_to_deposit(
deposit_date: date,
payment_data: PaymentCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
deposit = db.query(Deposit).filter(Deposit.deposit_date == deposit_date).first()
if not deposit:
raise HTTPException(status_code=404, detail="Deposit not found")
if not payment_data.file_no:
raise HTTPException(status_code=400, detail="file_no is required for payments")
file_obj = db.query(File).filter(File.file_no == payment_data.file_no).first()
if not file_obj:
raise HTTPException(status_code=404, detail="File not found")
# Create ledger entry first
ledger_entry = await _create_ledger_payment(
file_no=payment_data.file_no,
amount=payment_data.amount,
payment_date=deposit_date,
payment_method=payment_data.payment_method,
reference=payment_data.reference,
notes=payment_data.note,
apply_to_trust=payment_data.apply_to_trust,
empl_num=file_obj.empl_num,
db=db
)
# Create payment record
payment = Payment(
deposit_date=deposit_date,
file_no=payment_data.file_no,
client_id=payment_data.client_id,
regarding=payment_data.regarding,
amount=payment_data.amount,
note=payment_data.note
)
db.add(payment)
# Update deposit total
deposit.total += payment_data.amount
db.commit()
db.refresh(payment)
await _update_file_balances(file_obj, db)
return payment
@router.get("/deposits/", response_model=List[DepositResponse])
async def list_deposits(
start_date: Optional[date] = None,
end_date: Optional[date] = None,
include_payments: bool = False,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
query = db.query(Deposit)
if start_date:
query = query.filter(Deposit.deposit_date >= start_date)
if end_date:
query = query.filter(Deposit.deposit_date <= end_date)
query = query.order_by(Deposit.deposit_date.desc())
deposits = query.all()
results = []
for dep in deposits:
dep_data = {
"deposit_date": dep.deposit_date,
"total": dep.total,
"notes": dep.notes
}
if include_payments:
payments = db.query(Payment).filter(Payment.deposit_date == dep.deposit_date).all()
dep_data["payments"] = [p.__dict__ for p in payments]
results.append(dep_data)
return results
@router.get("/deposits/{deposit_date}", response_model=DepositResponse)
async def get_deposit(
deposit_date: date,
include_payments: bool = True,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
deposit = db.query(Deposit).filter(Deposit.deposit_date == deposit_date).first()
if not deposit:
raise HTTPException(status_code=404, detail="Deposit not found")
dep_data = {
"deposit_date": deposit.deposit_date,
"total": deposit.total,
"notes": deposit.notes
}
if include_payments:
payments = db.query(Payment).filter(Payment.deposit_date == deposit_date).all()
dep_data["payments"] = [p.__dict__ for p in payments]
return dep_data
@router.get("/reports/deposits")
async def get_deposit_report(
start_date: date,
end_date: date,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
deposits = db.query(Deposit).filter(
Deposit.deposit_date >= start_date,
Deposit.deposit_date <= end_date
).order_by(Deposit.deposit_date).all()
total_deposits = sum(d.total for d in deposits)
report = {
"period": {
"start": start_date.isoformat(),
"end": end_date.isoformat()
},
"total_deposits": total_deposits,
"deposit_count": len(deposits),
"deposits": [
{
"date": d.deposit_date.isoformat(),
"total": d.total,
"notes": d.notes,
"payment_count": db.query(Payment).filter(Payment.deposit_date == d.deposit_date).count()
} for d in deposits
]
}
return report