coming together
This commit is contained in:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user