Skip to documentation content
Steuerbuch

Professional Guide

Technical reference for accountants and tax advisors — accounting model, VAT treatments, exports and audit trail.

Steuerbuch Professional Guide

A technical reference for accountants (Buchhalter:innen), tax advisors (Steuerberater:innen) and in-house bookkeepers who run Steuerbuch day-to-day or take over client files at year-end.

This guide is a companion to the end-user User Guide and assumes you already know Austrian bookkeeping conventions (EKR, UStG, BAO, UGB §§224/231). Where the User Guide explains how to click, this document explains what Steuerbuch actually posts, reports and retains — and, just as importantly, what it does not do.

📸 Screenshot placeholders follow the same convention as the User Guide: drop images into docs/user_docs/images/ with the filename shown.

⚠️ Not legal or tax advice. Steuerbuch implements current Austrian UStG/UGB/EStG conventions but the responsibility for the correctness of any filing lies with the filer. Always verify against the current statute and BMF forms before submission.


Table of contents

  1. Audience & scope
  2. Accounting model
  3. Chart of accounts (Kontenrahmen)
  4. VAT treatments in depth
  5. UVA / U30 Kennzahlen reference
  6. Outgoing invoices — legal correctness & storno
  7. Collective invoices & delivery notes
  8. Depreciation & fixed assets (AfA)
  9. Stock valuation & recognition review
  10. Audit trail, period locks & retention
  11. Data export formats & column schemas
  12. Annual statement handover package
  13. API & automation surface
  14. Data model glossary
  15. Edge cases & gotchas
  16. Further reading

1. Audience & scope

Use this guide when you need to:

  • Take over a client's Steuerbuch tenant and reconcile opening balances.
  • Verify that UVA (Umsatzsteuervoranmeldung) figures tie back to the underlying invoices and journal.
  • Produce a UGB §224 Bilanz / §231 GuV or a §4(3) Einnahmen-Ausgaben-Rechnung.
  • Understand how Steuerbuch classifies a specific transaction for VAT / income tax purposes before relying on a generated figure.
  • Export bookkeeping data for transfer to a Steuerberater-side system.

If you are an end user looking for "where do I click", use the User Guide. If you want to understand Austrian VAT filing as a novice, start with the FAQ.

Screenshot: Accountant workspace overview

2. Accounting model

Steuerbuch supports both tax-relevant bookkeeping modes side-by-side:

Mode Persistence Recognition Primary reports
Einnahmen-Ausgaben-Rechnung (§4(3) EStG) Cash-basis aggregation over invoices Payment date (falls back to invoice date) E/A, UVA
Doppelte Buchführung (UGB §§189, 224, 231) Double-entry journal (journal_entries / journal_entry_lines) Invoice / document date Bilanz, GuV, Saldenliste, Balance List

The same invoice feeds both paths — there is no separate "accrual" invoice type. For accrual reporting, invoice completion and depreciation runs auto-post balanced journal entries; for E/A reporting, the same invoices are aggregated on a cash or document-date basis (configurable per report).

Key invariants (enforced in the service layer):

  • Every JournalEntry must have Σ debits == Σ credits to the cent.
  • Every JournalEntryLine has exactly one of debit or credit populated with a positive amount.
  • Posted entries are never mutated or deleted. Corrections are made via a reversing entry (reverses_entry_id) that preserves the full history.
  • A fresh posting can replace a reversed entry without violating the unique (source_ref_type, source_ref_id) constraint — the older row flips is_reversed=true first.

Source: backend/app/models/journal.py — read the module docstring if you want the canonical statement of invariants.


3. Chart of accounts (Kontenrahmen)

Steuerbuch ships the EKR 2024 (Einheitskontenrahmen Österreich) as system accounts (accounts.user_id IS NULL, is_system = true). The CSV source lives at docs/Kontenrahmenliste/ekr-kontenrahmen-oesterreich.csv and is loaded by Alembic migration 023 via app/services/ekr_loader.py.

3.1 Structure

Each account carries:

Column Meaning
number EKR number (e.g. 4000, 2100)
account_class 0–9 (EKR class)
statement "bilanz" or "guv"
normal_side "soll" / "haben"
bilanz_position_code UGB §224 position, e.g. A.II
guv_position_code UGB §231 position, e.g. GuV 1
vat_rate_default Default VAT rate for auto-classification
is_vat_deductible Drives Vorsteuer eligibility
koest_non_deductible_pct % added back to KöSt base (0 = fully deductible, 100 = fully non-deductible)

3.2 Customisation

User-owned rows override system rows by (user_id, number); the system row is preserved so a future EKR update can be re-seeded. You can:

  • Activate / deactivate an account (is_active).
  • Override the name, VAT default, or koest_non_deductible_pct.
  • Add a legal basis / note (legal_basis, note).

You cannot change statement, normal_side or bilanz_position_code on a system account — these feed UGB §224/§231 structure and are deliberately immutable.

3.3 Posting logic

When an invoice is completed, app/services/invoice_journal.py maps each line item to:

  1. A revenue / expense account (derived from invoice direction + category
    • line-item VAT rate, with account-level defaults overridden by line-item VAT if set).
  2. A VAT account (Umsatzsteuer for outgoing, Vorsteuer for incoming), keyed off the computed KZ (see §5).
  3. A counter-account (Debitoren / Kreditoren for accrual, or direct to bank when marked paid).

KöSt non-deductibility is applied at the report layer, not at posting — the full amount is posted to the P&L account, and koest_non_deductible_pct drives the tax-base adjustment row on the annual statement.


4. VAT treatments in depth

Every invoice carries a vat_treatment field (default "standard"). The enum is defined in backend/app/constants/uva_kz_catalog.py and the runtime mapping to U30 Kennzahlen lives in kz_contributions().

Treatment Meaning Legal basis Outgoing UVA effect Incoming UVA effect
standard Domestic VAT at 20/13/10/19 % UStG §10 KZ 000 + rate-specific base KZ + KZ 090 KZ 060 + KZ 095
reverse_charge Reverse-charge (recipient pays VAT) UStG §19 Abs 1a (Bauleistungen) KZ 000 + KZ 021 (net only, no VAT) KZ 057 (net) + KZ 090 + KZ 066 + KZ 095
eu_ic Intra-community delivery / acquisition Art 1 / Art 7 BMR KZ 000 + KZ 017 (net only) KZ 070 + rate-specific base (071/072/073) + KZ 090 + KZ 065 + KZ 095
export Export to non-EU UStG §6 Abs 1 Z 1 KZ 000 + KZ 011
import EUSt (Einfuhrumsatzsteuer) UStG §12 Abs 1 Z 2 KZ 061 + KZ 095
tax_free_other Other unecht / echt tax-free UStG §6 Abs 1 KZ 000 + KZ 020

4.1 Reverse-charge & IG acquisition: the "double posting"

For incoming reverse_charge and eu_ic invoices, the recipient is both the debtor (Umsatzsteuer in KZ 090) and the deductor (Vorsteuer in KZ 066 or KZ 065). Steuerbuch emits both contributions automatically, so the two cancel out in the net payable but appear on the respective lines of the U30 form — matching the form's reconciliation logic.

4.2 Setting the treatment

  • Manually on the invoice detail view (single select).
  • In bulk via the dashboard bulk-edit tool (POST /api/invoices/bulk-update).
  • At creation for outgoing invoices via the form.

Treatment is not inferred from line-item VAT rate alone — a 0 % line might be export, eu_ic or tax_free_other, with different UVA outcomes. Set the treatment explicitly if the default "standard" is wrong.

Screenshot: Invoice VAT treatment selector

5. UVA / U30 Kennzahlen reference

5.1 Catalog endpoint

GET /api/reports/uva-catalog

Returns every KZ Steuerbuch knows about, with code, category, law_ref (UStG paragraph), optional rate and a supported flag. Unsupported codes are still rendered (with value=0.0) so the form is legally complete — you can cross-check them against your own data if the client's activities require them.

Source of truth: KZ_CATALOG in backend/app/constants/uva_kz_catalog.py.

5.2 Supported KZ codes (as of this document)

Totals & aggregates

  • 000 — UStG §1, Gesamtbetrag der Entgelte (outgoing net sum)
  • 090 — UStG §20, Gesamtbetrag Umsatzsteuer
  • 095 — UStG §20, Gesamtbetrag Vorsteuer

Revenue bases per rate (outgoing, standard)

  • 022 — 20 % Normalsteuersatz (UStG §10 Abs 1)
  • 029 — 10 % ermäßigt (§10 Abs 2)
  • 006 — 13 % ermäßigt (§10 Abs 3)
  • 037 — 19 % Jungholz/Mittelberg (§10 Abs 4)
  • 044 — 10 % sonst. ermäßigt (§10 Abs 2)

Outgoing tax-free

  • 011 — Ausfuhr (§6 Abs 1 Z 1) — via export
  • 017 — Innergem. Lieferung (Art 7) — via eu_ic
  • 020 — Sonstige unecht befreit (§6 Abs 1) — via tax_free_other

Outgoing reverse-charge

  • 021 — Bauleistungen (§19 Abs 1a) — via reverse_charge

Incoming reverse-charge / IG / imports

  • 057 — Bauleistung Empfänger (§19 Abs 1) — incoming reverse_charge
  • 070 + 071 / 072 / 073 — IG Erwerb Basis per rate — incoming eu_ic
  • 061 — bezahlte EUSt (§12 Abs 1 Z 2) — incoming import

Input VAT (Vorsteuer)

  • 060 — §12 Abs 1 Z 1, Vorsteuer aus Rechnungen (standard domestic)
  • 065 — §12 Abs 1 Z 3, Vorsteuer aus IG Erwerb
  • 066 — §19, Vorsteuer reverse-charge Empfänger
  • 083 — §12 Abs 1 Z 2, verbuchte EUSt

5.3 Not (yet) supported

These codes appear in the catalog with supported=false and always contribute 0.00. If your client needs any of them, the figures must be added out-of-band to the filed U30:

012, 015, 018, 019 (outgoing tax-free special cases), 009, 049 (Landwirtschaft), 056, 063, 067, 089 (Berichtigungen / §12 Abs 3 Ausschluss), 076, 077 (IG special cases), 082 (§12 Abs 16 Pauschalierung).

5.4 Due-date formula

compute_due_date(year, period, periodicity) returns the filing deadline per UStG §21 Abs 1: 15th day of the second month following the reporting period. The rule is identical for monthly and quarterly filers — only the reporting window differs.

5.5 Consistency warnings

The overview tab flags common data issues before export:

  • Invoice with vat_total > 0 but treatment marked tax-free.
  • reverse_charge or eu_ic incoming invoice missing a rate.
  • A rate outside {0, 10, 13, 19, 20} on an invoice.
  • Period straddling an invoice-date change after booking.

Each warning drills through to the contributing invoices so you can fix the source rather than the aggregate.

5.6 Implementation reference

See docs/UVA_REPORT.md for the UI contract (tabs, export fields) and backend/app/services/report_service.py + report_export_service.py for the query and serialisation logic.


6.1 §11 UStG mandatory fields

Steuerbuch's outgoing-invoice form enforces the §11 Abs 1 UStG mandatory set:

  • Issuer name & address, issuer UID (if required)
  • Recipient name & address, recipient UID (for B2B EU + reverse-charge)
  • Sequential, gap-free invoice number (see §6.2)
  • Invoice date, delivery date / performance period
  • Quantity & description of goods/services
  • Net amount, VAT rate, VAT amount, gross amount
  • Reference to reverse-charge / IG / §6 if applicable

Reverse-charge invoices automatically carry the required note ("Steuerschuldnerschaft des Leistungsempfängers gem. §19 UStG") and suppress VAT amounts. IG-Lieferung invoices note Art 7 BMR and require a recipient UID.

6.2 Gap-free sequential numbering

Numbers are allocated from OutgoingInvoiceSequence (user_id, year, last_number, prefix) on finalise — not on creation. The sequence is unique per (user_id, year). A finalised invoice's number is immutable: a correction must be a reversing document, never an edit.

Implications:

  • Drafts don't burn numbers. Abandoning a draft is safe.
  • Testing. If you finalise an invoice in a test/demo environment you still consume a number — use a separate user or reset the sequence row.
  • Prefix changes. Changing prefix mid-year is allowed but visibly breaks the lexical ordering; most auditors prefer a prefix change at year boundary only.

6.3 Lifecycle & state

invoices.outgoing_state (and the analogous collective_invoices.outgoing_state) transitions through:

draft  ──►  sent  ──►  booked
  │           │           │
  └───────────┴───────►  voided (collective only)
  • draft — mutable, no number, PDF preview only.
  • sent — email sent (sent_at, sent_to, send_count tracked).
  • booked — final PDF frozen at generated_pdf_path, number locked, booked_at set, issuer_snapshot captured (so later changes to the company profile don't rewrite historical invoices).

6.4 Storno / reversal

Steuerbuch does not mutate a booked invoice. To correct one:

  1. Book a reversing outgoing invoice (negative amounts or a credit note, depending on your house style).
  2. The journal layer posts a corresponding JournalEntry with reverses_entry_id pointing at the original — preserving both sides for audit.
  3. The reversed entry flips is_reversed=true.
  4. If needed, book a fresh corrected invoice.

This is the mechanism behind the "Storno" action in the UI. The original PDF stays available; the reversal has its own number from the same sequence.

See docs/STATE_MACHINE.md §2 for the state diagram.


7. Collective invoices & delivery notes

7.1 Delivery notes (Lieferschein)

delivery_notes.status is draft | finalized. A finalised delivery note has its own gap-free number (DeliveryNoteSequence per (user_id, year)), captures quantities delivered, and can later be:

  • Billed individually (converted to a regular outgoing invoice), or
  • Included in a Sammelrechnung (collective invoice).

Finalised delivery notes are immutable; correct by issuing a reversing delivery note.

7.2 Collective invoices (Sammelrechnung)

A CollectiveInvoice references a client_id and a period window (date_range_from, date_range_to). Eligible source documents are the client's finalised delivery notes and any outgoing invoices flagged as "to be consolidated". On finalise:

  • A CollectiveInvoiceItem row is created per source document, snapshotting the line.
  • Each source document is flagged as "billed via collective #…" and becomes read-only for billing purposes — it cannot be double-billed.
  • issuer_snapshot and client_snapshot are frozen.
  • A booked_at timestamp is set; the PDF path is stored at generated_pdf_path.

7.3 VAT date

For both individual and collective outgoing invoices the VAT period is derived from invoice_date, not delivery date, unless the user explicitly sets a different performance period in the form. For E/A the cash date (payment date) takes precedence.

7.4 Voiding a collective

voided_at on CollectiveInvoice implements a soft storno: the source documents become billable again, a reversing journal entry is posted, and the booked PDF remains archived for audit purposes.


8. Depreciation & fixed assets (AfA)

Module: backend/app/models/depreciation.py + backend/app/services/depreciation_service.py + depreciation_journal.py.

8.1 Supported methods

Method Enum value Supported?
Linear AfA linear
Degressive AfA (EStG §7 Abs 1a) ❌ not implemented

If a client uses degressive AfA, calculate it externally and book it as a manual journal entry. DepreciationMethod only contains linear today — consult the source if this changes.

8.2 Half-year rule (Halbjahresabschreibung, EStG §7 Abs 2)

Enforced by depreciation_service.compute_annual_afa:

  • Acquisition in H1 (month ≤ 6): full AfA in year 1; final year runs from acquisition_year + useful_life_years − 1.
  • Acquisition in H2 (month ≥ 7): half AfA in year 1, half in final year; final year runs from acquisition_year + useful_life_years.

8.3 Disposal (Abgang) & private use

  • Disposal in-year → pro-rata AfA (months held / 12 × annual) booked as a disposal entry; asset status → disposed; gain/loss computed against disposal_proceeds.
  • Private-use portion (private_use_pct) creates private_use entries that reduce the deductible base for income tax.
  • Fully depreciated assets flip to fully_depreciated automatically.

8.4 GWG (geringwertige Wirtschaftsgüter)

Each asset has is_gwg and a configurable gwg_threshold (default €1,000 per the 2023+ EStG threshold; confirm the current statutory limit). A GWG is written off in full in year 1 via a single scheduled entry for the full acquisition_cost and the asset flips to fully_depreciated immediately.

8.5 Categories

AssetCategory is a user-scoped hierarchy with an optional default_useful_life_years and account_number (EKR). Categories feed defaults into the asset create form but do not constrain individual asset overrides.

8.6 Accounting impact

Each DepreciationEntry triggers a balanced JournalEntry posted to:

  • Debit the AfA P&L account (class 7 — Abschreibungen).
  • Credit the accumulated-depreciation B/S account (class 0/1 contra).

For disposals, a three-way entry books the residual book value, the proceeds (if any) and the gain/loss.

8.7 Report

GET /api/reports/afa-schedule/export (export_afa_schedule_xlsx) produces the full Anlagenverzeichnis per §7 Abs 3 EStG: asset number, name, acquisition date/cost, useful life, method, annual AfA, accumulated AfA, residual book value, disposal date, gain/loss.


9. Stock valuation & recognition review

9.1 Valuation methods

ValuationMethod supports fifo, lifo, wac (weighted average cost). Set per stock item. WAC is the default and the one most consistent with UGB §206 Abs 3 for homogeneous goods; FIFO is acceptable under UGB §206 Abs 2; LIFO is supported in the code but is not generally permissible for Austrian tax (UStG / EStG) — verify before selecting.

9.2 Weighted average recalculation

On every inbound transaction (purchase, adjustment_in, return_in, opening_balance):

new_avg_cost = (old_qty × old_avg + inbound_qty × inbound_unit_cost)
             / (old_qty + inbound_qty)

current_avg_cost is updated in place on the StockItem; the old value is preserved on prior outbound StockTransaction rows (each outbound stores the avg cost used for COGS so history is reproducible).

9.3 Transaction taxonomy

TransactionType — eight values grouped by direction:

  • Inbound: purchase, adjustment_in, return_in, opening_balance
  • Outbound: sale, adjustment_out, write_off, return_out

ReasonCode (optional): stocktake, damage, expiry, theft, donation, private_use, correction, other — drives the book-keeping narrative and, for donation / private_use, the §3 Abs 2 UStG deemed-supply handling.

9.4 Recognition review

When an incoming invoice is completed, stock_recognition_service attempts to match each line item to an existing StockItem (by SKU, then name, then fuzzy). A StockRecognition row is created with:

  • suggested_quantity and the inferred TransactionType
  • statuspending | accepted | rejected | merged | stale

Nothing is posted to stock until the user accepts the suggestion; on accept a StockTransaction is created, WAC is recalculated, and the recognition row flips to accepted. stale indicates the underlying invoice changed after the suggestion was generated.

9.5 Reversal

stock_service.reverse_transaction(txn_id) creates a mirrored StockTransaction (never deletes the original), recomputes WAC and, if the original triggered a journal entry, posts a reversing JournalEntry. Use this for corrections — do not DELETE transactions.

Known bug to watch (see repo memory): reversal of a sale passes the original unit_cost, which is None on sales. If you need stock reversals to fire cleanly, set unit_cost on outbound transactions.


10. Audit trail, period locks & retention

10.1 What is immutable

  • Booked outgoing invoices — number, PDF, issuer_snapshot, journal posting.
  • Finalised delivery notes — number, PDF.
  • Booked collective invoices — snapshots, PDF, number.
  • Posted journal entries — corrected only by reversing entry.
  • Completed stock transactions — reversed only by mirrored transaction.

10.2 Event logs

Domain-level append-only audit logs:

  • invoice_events — OCR run, field edit, link/unlink, finalise, reversal.
  • collective_invoice_events — builder actions, send, book, void.
  • delivery_note_events — create, finalise, consolidate.
  • journal_entries.description — free-text narrative per posting.

10.3 Retention

Steuerbuch retains all invoice PDFs, generated PDFs, journal lines and event logs for the duration of the subscription plus a 30-day read-only grace window if the subscription lapses, so the user can export before deletion.

This supports §132 BAO (7-year retention for books and records) only while the account is active. For long-term archival the user (or you, as their advisor) must run the ZIP export at least annually and store the archive in compliant storage.

10.4 GoBD-style characteristics

Steuerbuch is not GoBD-certified, but the journal model is designed to be consistent with GoBD's core principles:

Principle Implementation
Unveränderbarkeit reverses_entry_id, is_reversed; no UPDATE/DELETE on posted rows
Vollständigkeit Sequences enforce gap-freeness; event logs capture all domain actions
Nachvollziehbarkeit source_ref_type/source_ref_id link every journal row to its source document
Zeitgerechtigkeit posting_date is enforced at post time; edits flag the entry as reversed

11. Data export formats & column schemas

All exports are reached via GET …/export?format=xlsx|pdf under /api/reports/… and analogous resource routes.

11.1 Reports

Report Endpoint XLSX sheets Notable columns
E/A /api/reports/ea/export Einnahmen-Ausgaben Category, Net, VAT, Gross; VAT summary block (Output, Input, Net payable)
UVA /api/reports/uva/export Summary, Kennzahlen, Breakdown, Monthly trend, Top invoices Kennzahlen sheet: Category, Code, i18n_key, Law ref, Value; Breakdown: Rate %, Taxable, VAT
Saldenliste (Trial balance) /api/reports/trial-balance/export Saldenliste YYYY-period Account, Type, Opening, Debits, Credits, Closing; totals row + balanced flag
Balance list (per-account opening/closing) /api/reports/balance-list/export Balance list (section-filtered) Configurable visible sections (bilanz / guv)
Annual statement /api/reports/annual-statement/export Summary, per-section sheets Annual Revenue, Annual Expenses, plus UGB positions
AfA schedule /api/reports/afa-schedule/export Anlagenverzeichnis Asset number, name, acquisition date/cost, useful life, method, annual AfA, accumulated AfA, NBV, disposal

Implementation: backend/app/services/report_export_service.py (functions export_ea_xlsx, export_uva_xlsx, export_trial_balance_xlsx, export_annual_statement_xlsx, export_balance_list_xlsx, export_afa_schedule_xlsx and their _pdf counterparts).

Currency cells use a shared CURRENCY_FMT number format; negative closings are rendered red; headers carry bold styling.

11.2 Document exports

Resource Endpoint Notes
Invoice list GET /api/invoices/export XLSX with all fields; gated by FeatureGuard("export")
Invoice bulk import POST /api/invoices/import XLSX using GET /api/invoices/import-template
Stock items GET /api/stock/items/export FeatureGuard("stock")

11.3 Full ZIP export

Settings → Account → Download full export produces a ZIP containing:

  • All incoming invoice PDFs / images (original uploads).
  • All outgoing-invoice generated PDFs.
  • All collective-invoice and delivery-note PDFs.
  • XLSX dumps of every report above for the requested period.
  • A JSON manifest with entity IDs for cross-reference.

This is the recommended annual archival artefact for §132 BAO retention.


12. Annual statement handover package

For year-end handover to a Steuerberater:

  1. Lock the period. Verify no drafts remain; all invoices booked, all stock recognitions resolved, all AfA entries posted.
  2. Reconcile the UVA sum against the filed U30s (the monthly trend sheet makes this quick).
  3. Reconcile Saldenliste. Run GET /api/reports/trial-balance/export?year=YYYY&period=12 and confirm debits = credits and opening balances match the prior-year closing.
  4. Export the annual statement (/api/reports/annual-statement/export).
  5. Export the AfA schedule (/api/reports/afa-schedule/export).
  6. Run the full ZIP export (§11.3) and store it with the client's records.
  7. Invite your Steuerberater — multi-user / team accounts are on the roadmap; today, share an export or the client's login per your engagement letter.

If your client will switch to a different bookkeeping system next year, combine the Saldenliste (for opening balances) with the AfA schedule (for asset carry-forward) and the ZIP export (for archival of source documents).


13. API & automation surface

Steuerbuch today is a first-party web app. The HTTP API is primarily intended for the Steuerbuch frontend and not publicly versioned, but the following routes are stable enough to automate against. All require an authenticated session (Authorization: Bearer <token>).

13.1 Reports

GET /api/reports/{ea,uva,trial-balance,annual-statement,balance-list} return structured JSON (see backend/app/schemas/report.py). Each has a paired /export returning XLSX or PDF bytes.

GET /api/reports/uva-catalog returns the full Kennzahlen catalog.

13.2 Invoices

  • GET /api/invoices/export — XLSX dump.
  • GET /api/invoices/import-template — empty XLSX template.
  • POST /api/invoices/import — bulk XLSX import.
  • POST /api/invoices/bulk-update — multi-field update by ID list.
  • POST /api/invoices/bulk-delete — hard delete (only permitted for non-booked invoices).
  • POST /api/invoices/bulk-retry — re-run OCR on failed / pending.

13.3 Depreciation & stock

  • GET /api/reports/afa-schedule/export — XLSX.
  • GET /api/stock/items/export — XLSX.

13.4 Email upload

Each user receives a unique forwarding address. Emails to that address create UploadBatch rows and queue each attachment for OCR. Use this for zero-touch inbound invoice intake.

13.5 What's not available

  • Public GraphQL / REST schema with SLA — not yet.
  • Webhooks for third-party systems — not yet.
  • SFTP / EDI bulk ingest — not available.
  • Direct FinanzOnline submission — Steuerbuch produces figures only; filing happens in FinanzOnline.

Any of the above appearing in tender specs should be flagged as a roadmap item, not a current capability.


14. Data model glossary

Compact reference — see backend/app/models/ for field-level truth.

Entity Key identity Status machine Notes
User email active / disabled Owns everything via FK cascades
Invoice (user_id, invoice_number) + file_hash for dedup pending → processing → completed / failed / cancelled direction, vat_treatment, outgoing_state drive reports
InvoiceLineItem FK invoice_id Per-line vat_rate, net/gross
OutgoingInvoiceSequence (user_id, year) Gap-free number allocator
CollectiveInvoice (user_id, invoice_number) draft → sent → booked / voided Aggregates delivery notes + invoices
DeliveryNote (user_id, number) draft → finalized
StockItem (user_id, sku) Holds current_avg_cost, valuation method
StockTransaction id Never deleted; reversal = mirror transaction 8 types across 2 directions
StockRecognition id pending → accepted / rejected / merged / stale Bridge invoice → stock
FixedAsset (user_id, asset_number) active → fully_depreciated / disposed AfA driver
DepreciationEntry (asset_id, year) Entry type: scheduled / disposal / partial_writeoff / private_use
Account (user_id?, number) active / inactive System rows seeded from EKR CSV
JournalEntry id posted / reversed (is_reversed, reverses_entry_id) Double-entry, immutable
JournalEntryLine FK entry Exactly one of debit/credit per line
Folder (user_id, path) Free-form filing
Client (user_id, name) active / archived Recipient for outgoing docs

State machines in more detail: docs/STATE_MACHINE.md.


15. Edge cases & gotchas

  • Test tenants share sequences. Each user gets a fresh sequence starting at last_number=0, but any finalise call burns a number. Don't finalise dummy invoices in a production tenant.
  • Changing the UVA filing period mid-year. Supported, but the previously filed periods remain on their old cadence; the new cadence applies from the next unfiled period. Reconcile the handover period manually.
  • Client deletion with history. clients.ondelete=SET NULL on invoices — the client disappears, the invoices keep their numbers, but the invoice loses its client link. Archive the client instead of deleting it if you want the history to remain explorable.
  • Currency rounding. All monetary columns are NUMERIC(12,2) (or NUMERIC(14,2) for assets); WAC cost on stock is NUMERIC(14,6). UVA exports round to 2 decimals; internal computation retains full precision until the final write.
  • Duplicate false positives. Steuerbuch hashes file bytes plus key fields; a repeatedly-scanned receipt with different timestamps can duplicate. Mark duplicate_status='dismissed' to keep both.
  • Reversing a reversal. Posting a reverse of a reverses_entry_id entry is allowed and is the correct way to undo a mistaken reversal — the chain builds up, none of it is ever deleted.
  • LIFO selectable but not tax-valid. ValuationMethod.lifo exists as an enum value but LIFO is generally not permissible for Austrian income tax; select only on the advice of your tax advisor.
  • linear-only AfA. Degressive (§7 Abs 1a EStG) is not implemented; book manually if required.
  • Bulk delete of booked invoices. Blocked by the service layer — bulk-delete only works on drafts / pending / failed. Use storno for booked documents.

16. Further reading

Source modules worth skimming before disputing an export figure:

  • backend/app/constants/uva_kz_catalog.py — VAT treatments, KZ mapping, UStG §21 deadline
  • backend/app/models/journal.py — journal invariants
  • backend/app/models/account.py — EKR structure & KöSt hooks
  • backend/app/services/invoice_journal.py — invoice → journal posting
  • backend/app/services/depreciation_service.py — half-year rule, disposal pro-rata
  • backend/app/services/stock_valuation_service.py — WAC recomputation
  • backend/app/services/report_export_service.py — exact XLSX/PDF column layouts

If a figure looks wrong, the right debugging path is almost always: report → underlying journal entry → source document event log → invoice / asset / transaction. Everything is traceable.


Found an inaccuracy or a missing topic? Email support from the Billing page with "Professional Guide:" in the subject, or open an issue.