STRABAG Energy Invoice Analysis — Design

Demo branch for STRABAG client acquisition. No backward compatibility constraints.

Problem

STRABAG receives electricity invoices from many Austrian providers across hundreds of sites. Their controlling team needs to:

  1. Verify invoice correctness — rates, consumption math, category subtotals
  2. Analyze costs across sites — compare consumption and costs per Zählpunkt over time

Current Orcha ingestion treats energy invoices as generic invoices, losing domain-specific structure (meter readings, tariff categories, consumption comparisons).

Invoice Structure (from 6 sample PDFs)

All Austrian Strom invoices follow a consistent pattern:

Key identifiers: Zählpunkt (metering point ID), Netzebene (grid level), Anschlusswert/Anmeldeleistung (connection capacity), RC-KST codes (STRABAG cost centers).

Approach: Energy Schema as Separate Top-Level Section

Add energy-details as a sibling to line-items in structured data. Generic line items remain for accounting (debit/credit accounts, cost centers). The energy-specific UI reads from energy-details.

Small overlap between cost-category positions and line-items is intentional — line items carry accounting metadata, energy positions carry domain metadata (tariff rates, consumption basis).

Schema: energy-details

:energy-details
{:metering-points
 [{:zaehlpunkt     "AT0071000902000000000002124420101"
   :netzebene      5
   :anschlusswert  {:value 3000.0 :unit "kW"}
   :tarif          "BK Power Klagenfurt"
   :netzbetreiber  "Energie Klagenfurt GmbH"
   :address        "Leystraße 122, 1200 Wien"
   :meters
   [{:number "70275998"
     :readings
     [{:register    "HT"
       :period      {:from "2025-06-01" :to "2025-06-30"}
       :stand-alt   104.46
       :stand-neu   116.82
       :differenz   12.37
       :ableseart   "NBE"
       :faktor      3200
       :verbrauch   {:value 39580.80 :unit "kWh"}}]}]}]

 :consumption-summary
 {:current {:value 47120.00 :unit "kWh" :days 30 :per-day 1570.67}
  :prior   {:value 60617.60 :unit "kWh" :days 31 :per-day 1955.41}
  :delta   -13497.60}

 :cost-categories
 [{:category "energie"
   :subtotal 9457.45
   :positions
   [{:description "Energie"
     :period      {:from "2025-06-01" :to "2025-06-30"}
     :basis       {:value 47120.00 :unit "kWh"}
     :days        30
     :rate        {:value 19.737 :unit "Ct/kWh"}
     :amount      9300.07}]}
  {:category "netz"    :subtotal 5936.50 :positions [...]}
  {:category "abgaben" :subtotal 2349.03 :positions [...]}]}

Design decisions:

Extraction & Ingestion

Classification: Add "energy-invoice" to invoice-subtypes. The existing classification step already produces invoice-subtype — the LLM detects Zählpunkt, meter readings, Energie/Netz/Abgaben structure and classifies accordingly.

Extraction: When invoice-subtype is "energy-invoice", include energy-specific extraction instructions in the prompt. The prompt instructs the LLM to populate energy-details alongside standard fields and line items.

Line items: Still extracted in parallel from the same cost positions. They carry accounting metadata (debit/credit accounts, cost centers) while energy-details positions carry domain metadata.

Validation: Energy-specific rules:

UI: Per-Invoice Energy Detail View

Renders when invoice-subtype = "energy-invoice" and energy-details is present. Replaces the generic line items section.

Metering Point & Readings Card

Per metering point:

Cost Breakdown by Category

Three collapsible sections (Energie, Netz, Abgaben/Steuern):

Validation Badges

Energy-specific validations at the top:

Generic accounting section (debit/credit accounts, cost centers, accrual) still renders below from line-items.

UI: Energy Portfolio Overview

New route — entry point for energy controlling. Accessible from main navigation.

Summary Stats (Header)

4 aggregate numbers, update with filters:

Portfolio Table

Sortable, filterable table of all metering points across ingested energy invoices:

Column Source
Zählpunkt energy-details.metering-points[0].zaehlpunkt
Standort energy-details.metering-points[0].address or recipient address
Netzebene energy-details.metering-points[0].netzebene
Zeitraum Invoice billing period
Verbrauch (kWh) consumption-summary.current.value
Kosten (€ netto) Invoice subtotal
€/kWh Computed: subtotal / consumption
Δ Vorperiode consumption-summary.delta as percentage

Data source: query across documents where invoice-subtype = 'energy-invoice', reading from JSONB structured_data column. No separate tables.

Out of Scope