Taxxy Professional Guide
A technical reference for accountants (Buchhalter:innen), tax advisors (Steuerberater:innen) and in-house bookkeepers who run Taxxy 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 Taxxy 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. Taxxy 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
- Audience & scope
- Accounting model
- Chart of accounts (Kontenrahmen)
- VAT treatments in depth
- UVA / U30 Kennzahlen reference
- Outgoing invoices — legal correctness & storno
- Collective invoices & delivery notes
- Depreciation & fixed assets (AfA)
- Stock valuation & recognition review
- Audit trail, period locks & retention
- Data export formats & column schemas
- Annual statement handover package
- API & automation surface
- Data model glossary
- Edge cases & gotchas
- Further reading
1. Audience & scope
Use this guide when you need to:
- Take over a client's Taxxy 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 Taxxy 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.
2. Accounting model
Taxxy 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
JournalEntrymust haveΣ debits == Σ creditsto the cent. - Every
JournalEntryLinehas 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 flipsis_reversed=truefirst.
Source: backend/app/models/journal.py — read the module docstring if
you want the canonical statement of invariants.
3. Chart of accounts (Kontenrahmen)
Taxxy 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:
- A revenue / expense account (derived from invoice direction + category
- line-item VAT rate, with account-level defaults overridden by line-item VAT if set).
- A VAT account (
Umsatzsteuerfor outgoing,Vorsteuerfor incoming), keyed off the computed KZ (see §5). - A counter-account (
Debitoren/Kreditorenfor 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). Taxxy 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.
5. UVA / U30 Kennzahlen reference
5.1 Catalog endpoint
GET /api/reports/uva-catalog
Returns every KZ Taxxy 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 Umsatzsteuer095— 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) — viaexport017— Innergem. Lieferung (Art 7) — viaeu_ic020— Sonstige unecht befreit (§6 Abs 1) — viatax_free_other
Outgoing reverse-charge
021— Bauleistungen (§19 Abs 1a) — viareverse_charge
Incoming reverse-charge / IG / imports
057— Bauleistung Empfänger (§19 Abs 1) — incomingreverse_charge070+071/072/073— IG Erwerb Basis per rate — incomingeu_ic061— bezahlte EUSt (§12 Abs 1 Z 2) — incomingimport
Input VAT (Vorsteuer)
060— §12 Abs 1 Z 1, Vorsteuer aus Rechnungen (standard domestic)065— §12 Abs 1 Z 3, Vorsteuer aus IG Erwerb066— §19, Vorsteuer reverse-charge Empfänger083— §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 > 0but treatment marked tax-free. reverse_chargeoreu_icincoming 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. Outgoing invoices — legal correctness & storno
6.1 §11 UStG mandatory fields
Taxxy'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
prefixmid-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_counttracked).booked— final PDF frozen atgenerated_pdf_path, number locked,booked_atset,issuer_snapshotcaptured (so later changes to the company profile don't rewrite historical invoices).
6.4 Storno / reversal
Taxxy does not mutate a booked invoice. To correct one:
- Book a reversing outgoing invoice (negative amounts or a credit note, depending on your house style).
- The journal layer posts a corresponding
JournalEntrywithreverses_entry_idpointing at the original — preserving both sides for audit. - The reversed entry flips
is_reversed=true. - 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
CollectiveInvoiceItemrow 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_snapshotandclient_snapshotare frozen.- A
booked_attimestamp is set; the PDF path is stored atgenerated_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
disposalentry; asset status →disposed; gain/loss computed againstdisposal_proceeds. - Private-use portion (
private_use_pct) createsprivate_useentries that reduce the deductible base for income tax. - Fully depreciated assets flip to
fully_depreciatedautomatically.
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_quantityand the inferredTransactionTypestatus∈pending | 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
Taxxy 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
Taxxy 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:
- Lock the period. Verify no drafts remain; all invoices
booked, all stock recognitions resolved, all AfA entries posted. - Reconcile the UVA sum against the filed U30s (the monthly trend sheet makes this quick).
- Reconcile Saldenliste. Run
GET /api/reports/trial-balance/export?year=YYYY&period=12and confirm debits = credits and opening balances match the prior-year closing. - Export the annual statement (
/api/reports/annual-statement/export). - Export the AfA schedule (
/api/reports/afa-schedule/export). - Run the full ZIP export (§11.3) and store it with the client's records.
- 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
Taxxy today is a first-party web app. The HTTP API is primarily intended
for the Taxxy 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 onfailed/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 — Taxxy 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 |
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 NULLon 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)(orNUMERIC(14,2)for assets); WAC cost on stock isNUMERIC(14,6). UVA exports round to 2 decimals; internal computation retains full precision until the final write. - Duplicate false positives. Taxxy 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_identry 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.lifoexists 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
- End-user walkthrough:
USER_GUIDE.md - Plain-language Q&A:
FAQ.md - UVA UI contract & export fields:
../UVA_REPORT.md - State diagrams for every stateful domain:
../STATE_MACHINE.md - Design conventions and cross-cutting decisions:
../DESIGN_MANIFEST.md - EKR 2024 source data:
../Kontenrahmenliste/
Source modules worth skimming before disputing an export figure:
backend/app/constants/uva_kz_catalog.py— VAT treatments, KZ mapping, UStG §21 deadlinebackend/app/models/journal.py— journal invariantsbackend/app/models/account.py— EKR structure & KöSt hooksbackend/app/services/invoice_journal.py— invoice → journal postingbackend/app/services/depreciation_service.py— half-year rule, disposal pro-ratabackend/app/services/stock_valuation_service.py— WAC recomputationbackend/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.