package controllers import ( "fmt" "net/http" "path/filepath" "time" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // ExpenseController handles expense-related operations type ExpenseController struct { db *gorm.DB } // NewExpenseController creates a new expense controller func NewExpenseController(db *gorm.DB) *ExpenseController { return &ExpenseController{db: db} } // UploadReceipt handles receipt upload and OCR processing func (ec *ExpenseController) UploadReceipt(c *gin.Context) { // Get the uploaded file file, header, err := c.Request.FormFile("receipt") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "No file uploaded"}) return } defer file.Close() // Validate file type allowedTypes := []string{"image/jpeg", "image/jpg", "image/png", "application/pdf"} if !contains(allowedTypes, header.Header.Get("Content-Type")) { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file type. Only JPEG, PNG, and PDF files are allowed"}) return } // Generate unique filename ext := filepath.Ext(header.Filename) filename := fmt.Sprintf("receipt_%d%s", time.Now().UnixNano(), ext) // Save file to uploads directory filePath := filepath.Join("uploads", "receipts", filename) if err := c.SaveUploadedFile(header, filePath); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save file"}) return } // Perform OCR processing (placeholder) ocrData, accuracy, err := ec.performOCR(filePath) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "OCR processing failed"}) return } // Parse receipt data (placeholder) parsedData := parseReceiptData(ocrData) response := gin.H{ "file_path": filePath, "file_name": filename, "file_size": header.Size, "ocr_data": ocrData, "ocr_accuracy": accuracy, "parsed_data": parsedData, } c.JSON(http.StatusOK, gin.H{ "receipt": response, }) } // performOCR performs OCR on the uploaded file func (ec *ExpenseController) performOCR(filePath string) (string, float64, error) { // This is a placeholder for OCR implementation // In a real implementation, you would use Tesseract or another OCR service return "Sample OCR text", 95.0, nil } // parseReceiptData parses OCR text to extract structured data func parseReceiptData(ocrText string) map[string]interface{} { // This is a placeholder for receipt parsing logic // In a real implementation, you would use regex patterns or ML to extract: // - Merchant name // - Date // - Total amount // - VAT // - Items parsed := map[string]interface{}{ "merchant": "", "date": "", "total": 0, "vat": 0, "items": []map[string]interface{}{}, } return parsed } // Helper function to check if slice contains string func contains(slice []string, item string) bool { for _, s := range slice { if s == item { return true } } return false }