Add /transactions/import/image endpoint to extract transactions from images using AI
This commit is contained in:
@@ -155,6 +155,100 @@ async def import_quickbooks_transactions_csv(file: UploadFile = File(...)):
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/transactions/import/image", response_model=QuickBooksImportResponse)
|
||||
async def import_transactions_from_image(file: UploadFile = File(...)):
|
||||
"""
|
||||
Import transactions from an image (bank statement, credit card statement, etc.) using AI extraction.
|
||||
|
||||
This endpoint uses AI to extract transaction data from images like:
|
||||
- Bank statements
|
||||
- Credit card statements
|
||||
- Transaction lists
|
||||
- Financial documents
|
||||
"""
|
||||
try:
|
||||
# Validate file type
|
||||
allowed_types = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'pdf']
|
||||
file_extension = file.filename.split('.')[-1].lower()
|
||||
|
||||
if file_extension not in allowed_types:
|
||||
raise HTTPException(status_code=400, detail=f"Unsupported file type. Allowed: {allowed_types}")
|
||||
|
||||
# Read file content
|
||||
file_content = await file.read()
|
||||
|
||||
# Save file temporarily
|
||||
file_path = await document_processor.save_uploaded_file(file_content, file.filename)
|
||||
|
||||
# Use AI to extract transactions from the image
|
||||
extraction_result = await document_processor.extract_transactions_from_image(file_path)
|
||||
|
||||
if not extraction_result.get("extraction_success", False):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Failed to extract transactions from image: {extraction_result.get('error', 'Unknown error')}"
|
||||
)
|
||||
|
||||
# Parse extracted transactions
|
||||
transactions = []
|
||||
errors = []
|
||||
|
||||
extracted_transactions = extraction_result.get("transactions", [])
|
||||
|
||||
for idx, txn in enumerate(extracted_transactions):
|
||||
try:
|
||||
# Generate unique ID
|
||||
txn_id = f"img_{file.filename}_{idx+1}"
|
||||
|
||||
# Parse date
|
||||
txn_date = txn.get("date", "")
|
||||
if not txn_date:
|
||||
raise ValueError("No date found in transaction")
|
||||
|
||||
# Parse amount
|
||||
amount_str = str(txn.get("amount", "0"))
|
||||
amount = float(amount_str.replace('$', '').replace(',', '').strip())
|
||||
|
||||
# Get vendor/description
|
||||
payee_name = txn.get("vendor", txn.get("description", "Unknown"))
|
||||
|
||||
# Get memo/notes
|
||||
memo = txn.get("memo", txn.get("notes", ""))
|
||||
|
||||
transactions.append({
|
||||
"id": txn_id,
|
||||
"txn_date": txn_date,
|
||||
"amount": amount,
|
||||
"payee_name": payee_name,
|
||||
"memo": memo
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
errors.append(f"Transaction {idx+1}: {str(e)}")
|
||||
|
||||
if not transactions:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="No valid transactions could be extracted from the image"
|
||||
)
|
||||
|
||||
# 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)
|
||||
|
||||
# Attach errors from image parsing
|
||||
if hasattr(response, 'errors'):
|
||||
response.errors.extend(errors)
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
# ============================================================================
|
||||
# RECEIPT PROCESSING ENDPOINTS
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user