coming together

This commit is contained in:
HotSwapp
2025-08-13 18:53:35 -05:00
parent acc5155bf7
commit 5111079149
51 changed files with 14457 additions and 588 deletions

View File

@@ -294,33 +294,82 @@ async def _update_file_balances(file_obj: File, db: Session):
async def get_recent_time_entries(
days: int = Query(7, ge=1, le=30),
employee: Optional[str] = Query(None),
skip: int = Query(0, ge=0),
status: Optional[str] = Query(None, description="billed|unbilled"),
q: Optional[str] = Query(None, description="text search across description, file, employee, matter, client name"),
page: int = Query(1, ge=1),
limit: int = Query(50, ge=1, le=200),
sort_by: str = Query("date"),
sort_dir: str = Query("desc"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Get recent time entries across all files"""
"""Get recent time entries across all files with server-side sorting and pagination"""
cutoff_date = date.today() - timedelta(days=days)
query = db.query(Ledger)\
.options(joinedload(Ledger.file).joinedload(File.owner))\
# Base query with joins for sorting/searching by client/matter
base_query = db.query(Ledger) \
.join(File, Ledger.file_no == File.file_no) \
.outerjoin(Rolodex, File.id == Rolodex.id) \
.options(joinedload(Ledger.file).joinedload(File.owner)) \
.filter(and_(
Ledger.date >= cutoff_date,
Ledger.t_type == "2" # Time entries
))\
.order_by(desc(Ledger.date))
Ledger.t_type == "2"
))
if employee:
query = query.filter(Ledger.empl_num == employee)
entries = query.offset(skip).limit(limit).all()
base_query = base_query.filter(Ledger.empl_num == employee)
# Status/billed filtering
if status:
status_l = str(status).strip().lower()
if status_l in ("billed", "unbilled"):
billed_value = "Y" if status_l == "billed" else "N"
base_query = base_query.filter(Ledger.billed == billed_value)
# Text search across multiple fields
if q:
query_text = f"%{q.strip()}%"
base_query = base_query.filter(
or_(
Ledger.note.ilike(query_text),
Ledger.file_no.ilike(query_text),
Ledger.empl_num.ilike(query_text),
File.regarding.ilike(query_text),
Rolodex.first.ilike(query_text),
Rolodex.last.ilike(query_text)
)
)
# Sorting mapping (supported columns)
sort_map = {
"date": Ledger.date,
"file_no": Ledger.file_no,
"client_name": Rolodex.last, # best-effort: sort by client last name
"empl_num": Ledger.empl_num,
"quantity": Ledger.quantity,
"hours": Ledger.quantity, # alias
"rate": Ledger.rate,
"amount": Ledger.amount,
"billed": Ledger.billed,
"description": Ledger.note,
}
sort_column = sort_map.get(sort_by.lower(), Ledger.date)
direction = desc if str(sort_dir).lower() == "desc" else asc
# Total count for pagination (distinct on Ledger.id to avoid join-induced dupes)
total_count = base_query.with_entities(func.count(func.distinct(Ledger.id))).scalar()
# Apply sorting and pagination
offset = (page - 1) * limit
page_query = base_query.order_by(direction(sort_column)).offset(offset).limit(limit)
entries = page_query.all()
# Format results with file and client information
results = []
for entry in entries:
file_obj = entry.file
client = file_obj.owner if file_obj else None
results.append({
"id": entry.id,
"date": entry.date.isoformat(),
@@ -334,8 +383,15 @@ async def get_recent_time_entries(
"description": entry.note,
"billed": entry.billed == "Y"
})
return {"entries": results, "total_entries": len(results)}
return {
"entries": results,
"total_count": total_count,
"page": page,
"limit": limit,
"sort_by": sort_by,
"sort_dir": sort_dir,
}
@router.post("/time-entry/quick")