7.1 Comprehensive Field Validation Matrix


7.1.1 Customer Information Validation

FieldTypeMinMaxFormatRequiredValidation
customerIdString150Alphanumeric, dash, underscoreYes`^[A-Za-z0-9_-]+
personnummerString1013YYMMDD-XXXX or YYYYMMDD-XXXXConditionalLuhn algorithm
fullNameString1255Unicode printableYesNot empty, trim
emailString5255RFC 5322NoRegex + optional DNS MX
phoneString820E.164 recommendedNo`^+?[0-9\s-]+
streetString1255Any printableYes (postal)Not empty
postalCodeString510Country-specificYes (postal)Swedish: `^\d{3}\s?\d{2}
cityString1100Any printableYes (postal)Not empty
countryString22ISO 3166-1 alpha-2YesEnum: SE, NO, DK, FI


7.1.2 Financial Data Validation

FieldTypeMinMaxDecimalsRequiredValidation
subTotalDecimal0.00999999999.992Yes≥ 0
taxAmountDecimal0.00999999999.992Yes≥ 0
totalAmountDecimal0.01999999999.992Yes> 0
unitPriceDecimal0.00999999.992-6Yes≥ 0
quantityDecimal0.01999999.992Yes> 0
taxRateDecimal01001YesSwedish: 0, 6, 12, 25


7.1.3 Business Logic Validation Rules

RuleLogicError CodeMessage
Total ConsistencytotalAmount == subTotal + taxAmountAMOUNT_MISMATCHTotal must equal subtotal plus tax
Line Items Sumsum(lineItems.lineAmount) == subTotalLINE_ITEMS_MISMATCHLine items must sum to subtotal
Date LogicdueDate >= invoiceDateINVALID_DATE_RANGEDue date must be on or after invoice date
Tax Rate ValidtaxRate in [0, 6, 12, 25]INVALID_TAX_RATESwedish VAT: 0%, 6%, 12%, or 25%
Currency MatchAll amounts same currencyCURRENCY_MISMATCHAll amounts must use same currency
Personnummer LuhnLuhn checksumINVALID_PERSONNUMMERInvalid Swedish personnummer
Swedish Postal CodeFormat XXX XXINVALID_POSTAL_CODEFormat 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:

  1. XML parser throws exception during parse attempt
  2. Catch exception, extract line/column from error
  3. Do NOT store file in blob storage
  4. Log error to Application Insights with file details (no content)
  5. 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:

  1. Store file in blob for manual review
  2. Update batch status to "failed"
  3. Log unsupported format to Application Insights
  4. Send alert to support team
  5. 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:

CriterionValidation MethodTarget
Multi-region deployment operationalVerify services in both regionsBoth regions active
Traffic Manager routes to healthy regionSimulate West Europe failureRoutes to North Europe
Database auto-failover testedSimulate primary DB failureFailover < 15 min
Blob geo-replication verifiedWrite to primary, read from secondaryData replicated
Health checks on all servicesGET /health on all endpointsAll return 200
Automated incident alerts configuredSimulate service failureAlert received within 5 min
Worker auto-restart on crashKill worker processNew instance starts
Queue message retry testedSimulate worker crash mid-processingMessage reprocessed
Disaster recovery drill quarterlySimulate complete region lossRecovery within RTO
Backup restoration tested monthlyRestore database from backupSuccessful restore

Dependencies:

  • Azure Traffic Manager configuration
  • Multi-region resource deployment
  • Database replication setup
  • Automated failover testing procedures
  • Incident response runbook

Risks & Mitigation (Nordic Context):

RiskLikelihoodImpactMitigation StrategyOwner
Both Azure regions fail simultaneouslyVERY LOWCRITICAL- 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 regionsLOWHIGH- Each region operates independently
- Eventual consistency acceptable
- Manual reconciliation if partition >1 hour
- Traffic Manager handles routing
Technical Architect
Database failover causes brief downtimeLOWMEDIUM- 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 connectivityLOWMEDIUM- Azure datacenter redundancy within region
- Monitor Azure status dashboard
- Communication plan for customers
- No physical office connectivity required
Operations Manager



O

  • No labels