Add auto-matching endpoint and data storage for easier demo workflow

This commit is contained in:
Iyeoluwa Akinrinola
2025-07-02 21:02:11 +01:00
parent d04a7ce08b
commit 08386f8544
+111 -1
View File
@@ -41,6 +41,10 @@ drive_sync = GoogleDriveSync()
# In-memory storage for uploaded files (in production, use a database)
uploaded_files = {}
# Store imported transactions globally for easy access
stored_transactions = []
processed_receipts = {}
@app.get("/")
async def root():
"""Health check endpoint"""
@@ -137,6 +141,10 @@ async def import_quickbooks_transactions_csv(file: UploadFile = File(...)):
})
except Exception as e:
errors.append(f"Row {idx+1}: {str(e)}")
# Store transactions globally for auto-matching
global stored_transactions
stored_transactions = transactions
# Use the same logic as the JSON import endpoint
request_obj = QuickBooksImportRequest(transactions=transactions)
response = await import_quickbooks_transactions(request_obj)
@@ -220,6 +228,21 @@ async def process_document(file_id: str):
uploaded_files[file_id]["status"] = "processed"
uploaded_files[file_id]["extracted_data"] = result
# Store processed receipt data for auto-matching
global processed_receipts
processed_receipts[file_id] = {
"filename": file_info["filename"],
"upload_date": file_info["upload_date"],
"extraction_success": result.get("extraction_success", False),
"vendor": result.get("vendor"),
"total_amount": result.get("total_amount"),
"tax_amount": result.get("tax_amount"),
"date": result.get("date"),
"category": result.get("category"),
"confidence": result.get("confidence"),
"error": result.get("error")
}
return DocumentProcessResponse(
file_id=file_id,
extraction_success=result.get("extraction_success", False),
@@ -422,6 +445,74 @@ async def match_receipts_transactions(request: MatchingRequest):
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/match-auto", response_model=MatchingResponse)
async def match_auto():
"""
Automatically match all processed receipts against all imported transactions.
This endpoint uses the stored transaction data from CSV import and
all processed receipts to perform matching without requiring manual data input.
"""
try:
if not stored_transactions:
raise HTTPException(status_code=400, detail="No transactions imported. Please upload CSV first.")
if not processed_receipts:
raise HTTPException(status_code=400, detail="No receipts processed. Please upload and process receipts first.")
# Convert stored transactions to Receipt/Transaction models
transactions = [
Transaction(
id=t["id"],
transaction_date=datetime.strptime(t["txn_date"], "%Y-%m-%d"),
amount=abs(t["amount"]),
vendor=t["payee_name"],
notes=t.get("memo", "")
) for t in stored_transactions
]
receipts = []
for file_id, receipt_data in processed_receipts.items():
if receipt_data.get("extraction_success"):
receipts.append(Receipt(
id=file_id,
file_name=receipt_data.get("filename", ""),
upload_date=receipt_data.get("upload_date", datetime.now()),
receipt_date=datetime.strptime(receipt_data.get("date", "2024-01-01"), "%Y-%m-%d"),
amount=receipt_data.get("total_amount", 0.0),
tax=receipt_data.get("tax_amount", 0.0),
vendor=receipt_data.get("vendor", ""),
category=receipt_data.get("category", "")
))
if not receipts:
raise HTTPException(status_code=400, detail="No successfully processed receipts found.")
# Process matching using AI engine
matches = matching_engine.process_matching(receipts, transactions)
# Convert to response format
match_responses = [
MatchResponse(
receipt_id=match.receipt.id,
transaction_id=match.transaction.id,
confidence_score=match.confidence_score,
match_reason=match.match_reason,
receipt_vendor=match.receipt.vendor,
receipt_amount=match.receipt.amount,
transaction_vendor=match.transaction.vendor,
transaction_amount=match.transaction.amount
) for match in matches
]
# Get statistics
stats = matching_engine.get_matching_stats(matches)
return MatchingResponse(matches=match_responses, stats=stats)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/approve")
async def approve_match(request: ApprovalRequest):
"""
@@ -504,7 +595,26 @@ async def get_stats():
"recent_feedback_logs": len(recent_logs),
"active_rules": len([r for r in matching_engine.rules_engine.rules if r.status == "active"]),
"uploaded_documents": len(uploaded_files),
"processed_documents": len([f for f in uploaded_files.values() if f["status"] == "processed"])
"processed_documents": len([f for f in uploaded_files.values() if f["status"] == "processed"]),
"stored_transactions": len(stored_transactions),
"processed_receipts": len(processed_receipts)
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/status")
async def get_status():
"""Get current system status for demo purposes"""
try:
return {
"csv_uploaded": len(stored_transactions) > 0,
"transactions_count": len(stored_transactions),
"receipts_uploaded": len(uploaded_files),
"receipts_processed": len(processed_receipts),
"ready_for_matching": len(stored_transactions) > 0 and len(processed_receipts) > 0,
"sample_transactions": stored_transactions[:3] if stored_transactions else [],
"sample_receipts": list(processed_receipts.keys())[:3] if processed_receipts else []
}
except Exception as e: