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