A Ruby application that processes invoice images/PDFs, extracts data using Google Cloud Vision and Gemini AI, and creates expenses in Holded via their API.
- 📄 Process PDF invoices (converts to images automatically)
- 🤖 AI-powered data extraction using Google Cloud Vision + Gemini
- 💰 Automatic expense creation in Holded
- 📎 File attachment to Holded documents
- 🌐 Browser-based preview with embedded PDF viewer and editable fields side by side
- ✏️ Edit extracted data before confirming — expenses are created immediately on confirm
- 📁 Batch processing of multiple files in a single browser session
- 🧪 Test mode (
--test) to preview UI without consuming API tokens
bundle installThe application requires ImageMagick for PDF processing:
# macOS
brew install imagemagick
# Ubuntu/Debian
sudo apt-get install imagemagick
# CentOS/RHEL
sudo yum install ImageMagickCreate the following files in the keys/ directory:
- File:
keys/gemini_api_key.txt - Content: Your Gemini API key (just the key, no extra formatting)
- File:
keys/holded_api_key.txt - Content: Your Holded API key (just the key, no extra formatting)
The easiest way to run the processor is using the provided shell script:
# Process a single PDF file
./run_invoice_processor.sh invoice.pdf
# Process all PDF files in a directory
./run_invoice_processor.sh /path/to/invoices/
# Use custom API key paths
./run_invoice_processor.sh --google-credentials /path/to/credentials.json \
--gemini-key /path/to/gemini.txt \
--holded-key /path/to/holded.txt \
invoice.pdf
# Using environment variables
GOOGLE_CREDENTIALS_PATH=/custom/path.json \
GEMINI_API_KEY_PATH=/custom/gemini.txt \
HOLDED_API_KEY_PATH=/custom/holded.txt \
./run_invoice_processor.sh invoice.pdfYou can also run the processor directly with Ruby, but you'll need to set environment variables manually:
export GEMINI_API="$(cat keys/gemini_api_key.txt)"
export HOLDED_API_KEY="$(cat keys/holded_api_key.txt)"
ruby invoice_processor.rb invoice.pdf
# Test mode (mock Gemini data, no API tokens spent)
ruby invoice_processor.rb --test invoice.pdf
ruby invoice_processor.rb --test /path/to/invoices/- PDF Processing: Converts PDF to high-quality image using ImageMagick
- Data Extraction: Sends image to Gemini AI for structured data extraction
- Browser Preview: Opens a local web page with the PDF on the left and editable extracted data on the right. All fields can be corrected before confirming. For batch processing, all files are reviewed in a single browser session with automatic navigation between files.
- Holded Integration: Creates expense document in Holded immediately upon confirming each invoice, using the edited data.
- File Attachment: Attaches the original PDF to the Holded document
- Summary: Shows processing results both in the browser and in the terminal
The AI extracts the following information from invoices:
invoice_number: Invoice identifierdate: Invoice date (DD/MM/YYYY format)total_amount: Total invoice amountvendor_name: Supplier company namevendor_id: Supplier tax ID (CIF/NIF)tax_amount: Total tax amountline_items: Array of items with description, amount, and tax percentage
- ImageMagick not found: Install ImageMagick using your package manager
- API key errors: Ensure API key files exist and contain valid keys
- Permission denied: Make sure the script is executable:
chmod +x run_invoice_processor.sh - Google Cloud credentials: Ensure the service account has Vision API access
The application includes extensive logging. Check the console output for detailed information about each step.
holded/
├── run_invoice_processor.sh # Main runner script
├── invoice_processor.rb # Main processor
├── lib/ # Service classes
│ ├── vision_service.rb # Google Cloud Vision integration
│ ├── gemini_image_service.rb # Gemini AI integration
│ ├── holded_service.rb # Holded API integration
│ ├── invoice_processor.rb # Core processing logic
│ ├── preview_server.rb # Sinatra-based browser preview
│ └── mock_gemini_service.rb # Mock service for --test mode
├── keys/ # API keys and credentials
│ ├── arimidori-7a49f7f94661.json
│ ├── gemini_api_key.txt
│ └── holded_api_key.txt
└── test/ # Test files
└── invoices/ # Sample invoices for testing
Run the test suite:
bundle exec ruby -I test test/test_helper.rb- Never commit API keys to version control
- Use environment variables or secure key management in production
- The
keys/directory should be added to.gitignore - Consider using a secrets management service for production deployments