An Azure Function App built with C# .NET 8 that automatically processes invoices and sends payment reminders.
- Automatic Invoice Processing: Monitors Azure Blob Storage for uploaded invoices
- AI-Powered Data Extraction: Uses Azure AI Document Intelligence to extract invoice details
- Smart Validation: Validates extracted data with configurable confidence thresholds
- Scheduled Reminders: Sends email reminders 2 days before due date
- Retry Mechanism: Handles transient failures with exponential backoff
- Comprehensive Logging: Full audit trail in CosmosDB and Application Insights
The solution follows Clean Architecture principles with three main layers:
- Domain Models: Invoice, ExtractedData, ProcessingStatus, etc.
- Interfaces: Repository and service abstractions
- Enums: InvoiceStatus, EmailSendStatus, AttemptResult
- Exceptions: Custom exceptions for domain logic
- Repositories: CosmosDB data access
- Services:
- DocumentIntelligenceService (invoice parsing)
- AzureCommunicationEmailService (email sending via Azure Communication Services)
- InvoiceValidationService (data validation)
- InvoiceProcessingService (business logic orchestration)
- InvoiceUploadFunction: Blob trigger for processing new invoices
- EmailReminderFunction: Timer triggers for sending reminders
- .NET 8.0 SDK
- Azure Subscription with:
- Azure Storage Account (Blob Storage)
- Azure Cosmos DB Account
- Azure AI Document Intelligence Service
- Azure Communication Services (with Email enabled)
- Application Insights (recommended)
git clone <repository-url>
cd InvoiceReminderSystem# Create CosmosDB account
az cosmosdb create --name <account-name> --resource-group <rg-name>
# Create database
az cosmosdb sql database create --account-name <account-name> \
--name InvoiceReminderDb --resource-group <rg-name>
# Create container with partition key
az cosmosdb sql container create --account-name <account-name> \
--database-name InvoiceReminderDb --name Invoices \
--partition-key-path /invoiceMonth --resource-group <rg-name># Create storage account
az storage account create --name <storage-name> --resource-group <rg-name>
# Create blob container
az storage container create --name invoices \
--account-name <storage-name># Create Document Intelligence resource
az cognitiveservices account create --name <di-name> \
--resource-group <rg-name> --kind FormRecognizer \
--sku S0 --location <location>Update local.settings.json with your Azure resource credentials:
{
"Values": {
"CosmosDb:EndpointUri": "https://<your-account>.documents.azure.com:443/",
"CosmosDb:PrimaryKey": "<your-key>",
"DocumentIntelligence:Endpoint": "https://<your-di>.cognitiveservices.azure.com/",
"DocumentIntelligence:ApiKey": "<your-key>",
"AzureCommunicationServices:ConnectionString": "endpoint=https://<your-acs>.communication.azure.com/;accesskey=<key>",
"AzureCommunicationServices:SenderAddress": "DoNotReply@<your-domain>.com",
"BlobStorage:ConnectionString": "<your-connection-string>"
}
}# Restore dependencies
dotnet restore
# Build solution
dotnet build
# Run Function App
cd src/InvoiceReminderSystem.FunctionApp
func start# Create Function App
az functionapp create --name <function-app-name> \
--resource-group <rg-name> --storage-account <storage-name> \
--consumption-plan-location <location> --runtime dotnet-isolated \
--functions-version 4
# Deploy
func azure functionapp publish <function-app-name>
# Configure application settings in Azure Portal or via CLI
az functionapp config appsettings set --name <function-app-name> \
--resource-group <rg-name> --settings \
"CosmosDb:EndpointUri=<value>" \
"CosmosDb:PrimaryKey=<value>" \
# ... add all other settingsUpload a PDF invoice to the invoices container in your Blob Storage:
az storage blob upload --account-name <storage-name> \
--container-name invoices --name invoice-001.pdf \
--file ./path/to/invoice.pdfThe system will automatically:
- Detect the upload via Blob Trigger
- Extract invoice data using Document Intelligence
- Validate the extracted data
- Store in CosmosDB with scheduled reminder date
- Send email reminder 2 days before due date
View logs in Application Insights or check the CosmosDB container for invoice status:
Uploaded: Invoice receivedParsing: Extracting dataParsed: Data extractedValidated: Validation passedValidationFailed: Data quality issuesReadyToSend: Waiting for scheduled dateSending: Email in progressSent: Email deliveredSendFailed: Email delivery failed
Adjust minimum confidence for extracted data (default: 85%):
"DocumentIntelligence:MinimumConfidenceThreshold": "0.85"Configure retry attempts (default: 3):
"SendGrid:MaxRetryAttempts": "3"Modify CRON expressions in EmailReminderFunction.cs:
// Daily at 9 AM UTC
[TimerTrigger("0 0 9 * * *")]
// Every 5 minutes for retries
[TimerTrigger("0 */5 * * * *")]- Repository Pattern: Abstracts data access logic
- Dependency Injection: Loose coupling and testability
- SOLID Principles:
- Single Responsibility: Each class has one job
- Open/Closed: Extensible without modification
- Liskov Substitution: Interfaces are substitutable
- Interface Segregation: Small, focused interfaces
- Dependency Inversion: Depend on abstractions
Invoices with low confidence scores or missing data are marked as ValidationFailed and logged for manual review.
Temporary issues (rate limits, network errors) trigger automatic retries with exponential backoff:
- Attempt 1: Retry after 1 minute
- Attempt 2: Retry after 5 minutes
- Attempt 3: Retry after 15 minutes
Issues like invalid email addresses are marked as SendFailed without retries.
Set up Azure Monitor alerts for:
- High validation failure rates
- Email delivery failures
- Function execution failures
Example query for failed invoices:
InvoiceContainer
| where processingStatus.currentStatus == "ValidationFailed"
| project id, blobMetadata.blobName, processingStatus.validationErrors# Add test project
dotnet new xunit -n InvoiceReminderSystem.Tests
# Run tests
dotnet testUse Azurite for local storage emulation and CosmosDB Emulator for database testing.
Solution: The prebuilt invoice model may not extract email addresses reliably. Consider:
- Training a custom model with your invoice formats
- Adding email to a standard location in your invoices
- Using a different field (e.g., vendor contact info)
Solution: Ensure proper indexing:
{
"indexingPolicy": {
"includedPaths": [
{ "path": "/processingStatus/currentStatus/?" },
{ "path": "/emailNotification/scheduledSendDate/?" },
{ "path": "/emailNotification/sendStatus/?" }
]
}
}Solution: Check timer function logs and ensure the scheduled send date is set correctly. Verify the CRON expression matches your timezone needs.
- Follow SOLID principles
- Add unit tests for new features
- Update documentation
- Use meaningful commit messages
[Your License Here]
For issues and questions, please create an issue in the repository.