# Vermietungs-Tool A local web app for running a small room-rental business in Austria: the guest register, the room ledger, invoices, the monthly tourist-tax report and the quarterly VAT list, all from one database. It is built for one non-technical user — German UI, no training needed — and handles the bookkeeping rules that make this work tedious by hand. This repository presents the tool. It contains the application code only; it ships no data. ![Belegungsplan — the occupancy timeline](docs/screenshots/plan.png) *All names, companies and addresses in the screenshots are fictitious.* ## What it does | Page | Purpose | | --- | --- | | **Gästeblätter** | The guest register: one registration sheet per guest and stay (Meldegesetz), with room, employer, main residence and tourist-tax liability. A room change continues the same sheet in another room. | | **Zimmerbelegung** | The room ledger: what each room is billed for, per period and person — weeks, nights or months, Ortstaxe, payer, invoice number and payments (also in parts). | | **Belegungsplan** | A rooms × days timeline of who is in the house, today's free beds, and bookings ahead. | | **Rechnungen** | Invoices generated from ledger or register rows: per-person lines, VAT breakdown, cancellation fees as non-taxable damages, Ortstaxe, A4 layout for window envelopes, PDF download. | | **Firmen** | Companies with all their spellings and address versions; old invoices keep the address that was true at the time. | | **TVB-Meldung** | The monthly tourist-tax report (Sammelmeldung) per business, ready to type into Feratel: persons and nights by country or Austrian/German region, liable and exempt. | | **Umsatzsteuer** | The quarterly VAT list (cash basis): every receipt in the quarter it was paid, part payments split across quarters, non-taxable items kept out of the base. | | **Datenprüfung** | Data-quality checks — register and ledger disagreeing, departures never billed, recipients without an address, duplicate invoice numbers — and merge suggestions for near-duplicate companies and guests. | ### Rules it applies so nobody has to - **Billing in weeks**: a stay of four nights or more is billed in whole weeks, rounded up; shorter stays are billed by the night. - **Ortstaxe**: charged per person and night at the rate in force on the arrival date, and waived for professional stays of more than ten nights — judged on the *whole* stay, across room changes and month-end invoices, and never for a guest who is still in the house. - **The ledger follows the register**: a departure entered on a guest sheet closes that guest's ledger row with quantity, amount and Ortstaxe; a guest leaving before the others gets a row of their own, and rows that end up alike merge again. Month-end billing closes a row and continues the stay in a new one. - **The ledger follows the invoice**: when an invoice settles a price or period differently, the ledger rows it was made from are corrected to match; when a ledger row changes after invoicing, the invoice is flagged and can be regenerated in one click. - **Issued invoices never change as a side effect**: recording a payment touches only the payment fields, and payment information from the ledger is offered, not applied. - **Invoice numbers follow the dates** within each business and year; saving an invoice out of order, or one whose recipient has no postal address, asks first. - **Payments**: a date in "bezahlt am" marks a row paid, payments in parts keep it open until amount and Ortstaxe are covered, and a paid invoice pays its ledger rows. ## Screenshots | | | | --- | --- | | ![Zimmerbelegung](docs/screenshots/ledger.png) | ![Gästeblätter](docs/screenshots/register.png) | | **Zimmerbelegung** — the room ledger | **Gästeblätter** — the guest register | | ![TVB-Meldung](docs/screenshots/tvb.png) | ![Datenprüfung](docs/screenshots/quality.png) | | **TVB-Meldung** — the monthly tourist-tax report | **Datenprüfung** — data-quality checks | ![Rechnung](docs/screenshots/invoice.png) ## Under the hood - **Flask + Jinja2**, server-rendered pages; all UI text comes from [locales/de.json](locales/de.json). - **PostgreSQL** through psycopg, behind a thin adapter ([app/db.py](app/db.py)) that gives the code a sqlite3-style interface (`?` placeholders, `lastrowid`, rows by name or index). Two roles: the app's role may only read and write data; the owner applies migrations and takes snapshots. - **Single source of truth**: every value lives in one place; totals, nights, quantities and payment states are computed at read time ([app/compute.py](app/compute.py), [app/repo.py](app/repo.py)). Money is summed at full precision and rounded once. - **Audit log** ([migrations/002_audit_log.sql](migrations/002_audit_log.sql), [app/audit.py](app/audit.py)): a trigger records every row change with before/after values and who made it; the app's role can read the log but not alter it. `python -m app.audit revert` undoes chosen entries or a whole transaction, refusing if the rows changed since. - **A grid engine in plain JavaScript** ([app/static/grids.js](app/static/grids.js)) for lists of thousands of rows: rows ship in an inert `