7.1 Comprehensive Field Validation Matrix
7.1.1 Customer Information Validation
| Field | Type | Min | Max | Format | Required | Validation |
|---|---|---|---|---|---|---|
| customerId | String | 1 | 50 | Alphanumeric, dash, underscore | Yes | `^[A-Za-z0-9_-]+ |
| personnummer | String | 10 | 13 | YYMMDD-XXXX or YYYYMMDD-XXXX | Conditional | Luhn algorithm |
| fullName | String | 1 | 255 | Unicode printable | Yes | Not empty, trim |
| String | 5 | 255 | RFC 5322 | No | Regex + optional DNS MX | |
| phone | String | 8 | 20 | E.164 recommended | No | `^+?[0-9\s-]+ |
| street | String | 1 | 255 | Any printable | Yes (postal) | Not empty |
| postalCode | String | 5 | 10 | Country-specific | Yes (postal) | Swedish: `^\d{3}\s?\d{2} |
| city | String | 1 | 100 | Any printable | Yes (postal) | Not empty |
| country | String | 2 | 2 | ISO 3166-1 alpha-2 | Yes | Enum: SE, NO, DK, FI |
7.1.2 Financial Data Validation
| Field | Type | Min | Max | Decimals | Required | Validation |
|---|---|---|---|---|---|---|
| subTotal | Decimal | 0.00 | 999999999.99 | 2 | Yes | ≥ 0 |
| taxAmount | Decimal | 0.00 | 999999999.99 | 2 | Yes | ≥ 0 |
| totalAmount | Decimal | 0.01 | 999999999.99 | 2 | Yes | > 0 |
| unitPrice | Decimal | 0.00 | 999999.99 | 2-6 | Yes | ≥ 0 |
| quantity | Decimal | 0.01 | 999999.99 | 2 | Yes | > 0 |
| taxRate | Decimal | 0 | 100 | 1 | Yes | Swedish: 0, 6, 12, 25 |
7.1.3 Business Logic Validation Rules
| Rule | Logic | Error Code | Message |
|---|---|---|---|
| Total Consistency | totalAmount == subTotal + taxAmount | AMOUNT_MISMATCH | Total must equal subtotal plus tax |
| Line Items Sum | sum(lineItems.lineAmount) == subTotal | LINE_ITEMS_MISMATCH | Line items must sum to subtotal |
| Date Logic | dueDate >= invoiceDate | INVALID_DATE_RANGE | Due date must be on or after invoice date |
| Tax Rate Valid | taxRate in [0, 6, 12, 25] | INVALID_TAX_RATE | Swedish VAT: 0%, 6%, 12%, or 25% |
| Currency Match | All amounts same currency | CURRENCY_MISMATCH | All amounts must use same currency |
| Personnummer Luhn | Luhn checksum | INVALID_PERSONNUMMER | Invalid Swedish personnummer |
| Swedish Postal Code | Format XXX XX | INVALID_POSTAL_CODE | Format must be: XXX XX |
7.2 Error Handling Scenarios
7.2.1 Scenario: Malformed XML Upload
Trigger: User uploads non-well-formed XML
System Action:
- XML parser throws exception during parse attempt
- Catch exception, extract line/column from error
- Do NOT store file in blob storage
- Log error to Application Insights with file details (no content)
- Return 400 Bad Request with detailed error
Response:
{
"success": false,
"errors": [{
"code": "INVALID_XML",
"message": "XML file is not well-formed",
"field": "file",
"details": {
"line": 142,
"column": 23,
"error": "Unexpected end tag: </Invoice>. Expected: </InvoiceHeader>",
"suggestion": "Verify all XML tags are properly closed and nested",
"documentationUrl": "https://docs.egflow.com/xml-format"
}
}]
}
7.2.2 Scenario: Unsupported Vendor Format
Trigger: XML namespace doesn't match GASEL, XELLENT, or ZYNERGY
System Action:
- Store file in blob for manual review
- Update batch status to "failed"
- Log unsupported format to Application Insights
- Send alert to support team
- Return 415 Unsupported Media Type
Response:
{
"success": false,
"errors": [{
"code": "UNSUPPORTED_FORMAT",
"message": "Cannot detect vendor format",
"details": {
"detectedNamespace": "http://custom-vendor.com/invoices",
"rootElement": "InvoiceBatch",
"supportedFormats": [
{
"vendorCode": "GASEL",
"namespace": "urn:ediel:se:electricity:invoice:1.0",
"description": "Telinet Energi / EDIEL format"
},
{
"vendorCode": "XELLENT",
"namespace": "http://rep.oio.dk/ubl/xml/schemas/0p71/pie/",
"description": "Karlskoga Energi / OIOXML format"
},
{
"vendorCode": "ZYNERGY",
"namespace": "http://eg.dk/Zynergy/1.0/invoice.xsd",
"description": "EG Software Zynergy format"
}
],
"suggestion": "Contact EG Support to add support for your vendor format",
"supportEmail": "support@egflow.com"
}
}]
}
7.2.3 Scenario: Template Rendering Failure
Trigger: Handlebars template references undefined variable
System Action:
1. DocumentGenerator attempts to render template
2. Handlebars throws exception: Variable 'customer.address.street' not found
3. Log error with full context (template24h to authorities)<br>- Security measures documentation<br>- Annual security audit<br>- CISO designated | Legal/Compliance |
| Swedish Säkerhetspolisen (SÄPO) requirements | LOW | HIGH | - Enhanced security for critical infrastructure<br>- Incident reporting to MSB (Swedish Civil Contingencies)<br>- Employee background checks for production access<br>- Security clearance for key personnel | Security Officer |
| API key theft/leakage | MEDIUM | HIGH | - Rotate keys every 90 days<br>- Monitor for leaked keys (GitHub scanning)<br>- Revoke compromised keys immediately<br>- API key hashing in database<br>- Never log full API keys | Security Officer |
| Insider threat (privileged access abuse) | LOW | CRITICAL | - Least privilege principle<br>- All actions audited<br>- Regular access reviews<br>- Separation of duties<br>- Anomaly detection in audit logs | Security Officer |
| Third-party vendor breach (SendGrid, 21G) | LOW | HIGH | - Data Processing Agreements (DPAs) signed<br>- Regular vendor security assessments<br>- Minimal data sharing<br>- Encryption in transit to vendors<br>- Vendor breach response plan | Legal/Compliance |
---
## 4.5 NFR-005: Data Retention & Lifecycle Management
**Requirement:** The system shall manage data retention according to Swedish Bokföringslagen (7-year invoice retention) with automated lifecycle policies for cost optimization through storage tier transitions.
**Priority:** **HIGH**
**Retention Policies:**
| Data Type | Legal Requirement | Retention Period | Storage Tier Transition | Disposal Method |
|-----------|------------------|-----------------|------------------------|-----------------|
| **Invoices (PDF/HTML/JSON)** | Bokföringslagen (Swedish Accounting Act) | 7 years from fiscal year end | Day 0-365: Hot<br>Day 366-2555: Cool<br>Day 2556+: Archive | Permanent deletion after 7 years |
| **Batch Source Files (XML)** | None (internal processing) | 90 days | Day 0-30: Hot<br>Day 31-90: Cool<br>Day 91+: Delete | Automatic deletion |
| **Batch Metadata JSON** | Audit trail | 90 days | Day 0-90: Hot<br>Day 91+: Delete | Automatic deletion |
| **Audit Logs (PostgreSQL)** | GDPR, Swedish law | 7 years | Year 0-1: PostgreSQL<br>Year 1-7: Blob (compressed) | Deletion after 7 years |
| **Application Logs** | Operational | 90 days | Application Insights | Automatic deletion |
| **Templates** | Business continuity | Indefinite (archived versions) | Hot (active)<br>Cool (archived) | Never deleted |
| **Organization Config** | Business continuity | Indefinite | Hot | Never deleted (updated in place) |
**Azure Blob Lifecycle Policy:**
```json
{
"rules": [
{
"enable---
## 4.3 NFR-003: Availability & Reliability (Nordic 24/7 Operations)
**Requirement:** The system shall maintain 99.9% uptime with automatic failover, multi-region deployment, and recovery procedures to support Nordic utilities' 24/7 invoice delivery operations.
**Priority:** **HIGH**
**Availability Targets:**
| Metric | Target | Allowed Downtime | Measurement | Consequences of Breach |
|--------|--------|-----------------|-------------|----------------------|
| **System Uptime** | 99.9% | 43 min/month | Azure Monitor | SLA credit to customers |
| **Batch Success Rate** | > 99.5% | 50 failures per 10K | Processing logs | Investigation required |
| **Delivery Success Rate** | > 98% | 200 failures per 10K | Delivery tracking | Alert to organization |
| **API Availability** | 99.9% | 43 min/month | Health check monitoring | Incident escalation |
| **MTTR (Mean Time To Recovery)** | < 30 minutes | N/A | Incident timestamps | Process improvement |
| **MTBF (Mean Time Between Failures)** | > 720 hours (30 days) | N/A | Incident tracking | Root cause analysis |
**Multi-Region Deployment:**
Primary Region: West Europe (Azure westeurope)
- Sweden: Primary processing
- Denmark: Primary processing
Secondary Region: North Europe (Azure northeurope)
- Norway: Primary processing
- Finland: Primary processing
- Failover for Sweden/Denmark
Traffic Routing:
- Azure Traffic Manager with Performance routing
- Health check: /health endpoint every 30 seconds
- Auto-failover on 3 consecutive failed health checks
- Failover time: < 2 minutes
**Recovery Time Objectives:**
| Scenario | RTO (Recovery Time) | RPO (Data Loss) | Recovery Method | Responsible Team |
|----------|---------------------|-----------------|-----------------|------------------|
| **Worker Instance Crash** | < 5 minutes | 0 (idempotent) | Automatic queue retry | Automatic |
| **Database Failure** | < 15 minutes | < 5 minutes | Auto-failover to read replica | Automatic + Ops verification |
| **Primary Region Failure** | < 30 minutes | < 15 minutes | Traffic Manager failover to secondary region | Ops Manager |
| **Blob Storage Corruption** | < 1 hour | < 1 hour | Restore from blob version/snapshot | Ops Team |
| **Queue Service Outage** | < 15 minutes | 0 (messages preserved) | Wait for Azure recovery | Ops Manager |
| **SendGrid Complete Outage** | < 2 hours | 0 (fallback to postal) | Route all to postal queue | Ops Team |
| **21G SFTP Unavailable** | < 4 hours | 0 (retry at next scheduled run) | Retry at 12:00 or 20:00 | Ops Team |
**Backup & Recovery Strategy:**
**Blob Storage:**
```yaml
Replication: Geo-Redundant Storage (GRS)
- Primary: West Europe
- Secondary: North Europe
- Automatic replication
Soft Delete: 7 days retention
- Recover accidentally deleted blobs within 7 days
Blob Versioning: 30 days retention
- Previous versions accessible
- Rollback capability
Point-in-Time Restore: Not needed (versioning sufficient)
PostgreSQL:
Backup Schedule: Daily automated backups Retention: 35 days Backup Window: 02:00-04:00 CET (low traffic period) Point-in-Time Restore: 7 days Geo-Redundant: Enabled Read Replica: North Europe (for failover)
Acceptance Criteria:
| Criterion | Validation Method | Target |
|---|---|---|
| Multi-region deployment operational | Verify services in both regions | Both regions active |
| Traffic Manager routes to healthy region | Simulate West Europe failure | Routes to North Europe |
| Database auto-failover tested | Simulate primary DB failure | Failover < 15 min |
| Blob geo-replication verified | Write to primary, read from secondary | Data replicated |
| Health checks on all services | GET /health on all endpoints | All return 200 |
| Automated incident alerts configured | Simulate service failure | Alert received within 5 min |
| Worker auto-restart on crash | Kill worker process | New instance starts |
| Queue message retry tested | Simulate worker crash mid-processing | Message reprocessed |
| Disaster recovery drill quarterly | Simulate complete region loss | Recovery within RTO |
| Backup restoration tested monthly | Restore database from backup | Successful restore |
Dependencies:
- Azure Traffic Manager configuration
- Multi-region resource deployment
- Database replication setup
- Automated failover testing procedures
- Incident response runbook
Risks & Mitigation (Nordic Context):
| Risk | Likelihood | Impact | Mitigation Strategy | Owner |
|---|---|---|---|---|
| Both Azure regions fail simultaneously | VERY LOW | CRITICAL | - Extremely rare (Azure multi-region SLA 99.99%) - Accept risk (probability vs cost of 3rd region) - Communication plan for extended outage - Manual failover to Azure Germany (emergency) | Executive Sponsor |
| Network partition between regions | LOW | HIGH | - Each region operates independently - Eventual consistency acceptable - Manual reconciliation if partition >1 hour - Traffic Manager handles routing | Technical Architect |
| Database failover causes brief downtime | LOW | MEDIUM | - Accept 1-2 minutes downtime during failover - API returns 503 with Retry-After - Queue-based processing unaffected - Monitor failover duration | Operations Manager |
| Swedish winter storms affect connectivity | LOW | MEDIUM | - Azure datacenter redundancy within region - Monitor Azure status dashboard - Communication plan for customers - No physical office connectivity required | Operations Manager |
O