# Budget & Capex A self-hosted web app for building yearly department budgets and the "Capex e Custos" cost report. Users enter cost lines in the browser; the app produces the monthly budget (like the *Budget IT* sheet) and the cost report (like *Capex_e_Custos_2027*), online and as PDF and Excel. The interface and the reports are available in Portuguese and English. ## What's inside | Container | Role | | --- | --- | | `web` | Caddy: serves the web app and forwards `/api` to the API. Optional HTTPS. | | `api` | Python (FastAPI): business rules, Excel import, PDF and Excel exports. | | `db` | PostgreSQL 16. All data lives in the `db-data` Docker volume. | | `backup` | Dumps the database every night to `./backups` and keeps 30 days. | ## Install You need a server on your network with Docker Engine and the Docker Compose plugin. ```sh # 1. copy this folder to the server, then inside it: cp .env.example .env # 2. edit .env: set POSTGRES_PASSWORD, SECRET_KEY (openssl rand -hex 32) and ADMIN_PASSWORD # 3. build and start docker compose up -d --build ``` Open `http://:8080` and sign in with `ADMIN_EMAIL` / `ADMIN_PASSWORD` from `.env`. Change the password under **My profile** right away. The admin account is created only on the first start; changing `.env` later does not change it. ## First steps 1. **Departments**: the app starts with an **IT** department. Add the others (F&B, Rooms, …); each new department starts from the default chart of accounts, from no categories, or from a copy of another department's categories. 2. **Budgets → Import Excel**: choose the department and upload `Budget para 2027.xlsx` (a copy is in `backend/tests/fixtures/`) or a filled-in template (see below). The import reads the *Sheet1* and *Budget IT* sheets: 54 lines, the 5.5% rate, every multiplier (including the ×2.055 and ×3.055 hardware increases) and the remarks. Formulas such as `=5512*12` are kept as typed calculations. 3. **Users → New user** for each colleague, with their department and role: * **Viewer**: view and export every budget. * **Upper Management**: view, download and email the budgets and reports of every department, switching between departments; changes nothing. A department is optional. * **Editor**: edit their own department's draft budgets and submit them for review. * **Approver**: also approve their department's budgets and manage its categories. * **Administrator**: everything, including departments, workflows, users and sites. Everyone can view every department's budgets and reports; only the budget's own department can change it, and only the people named in its workflow can approve it. Each user can turn on **MFA** in *My profile* (authenticator app + recovery codes). If someone loses their phone, an administrator opens *Users → Edit* and presses **Reset MFA**. MFA secrets are encrypted with `SECRET_KEY`: keep that value when you upgrade, or everyone's MFA has to be reset. 4. **Reports**: pick the year, language and optionally one site, then *View online*, *PDF* or *Excel*. ## Entering data * Each line has a **prior-year actual**, a **base** for the new year and an **adjustment**: *year rate* (the "Inflation + adjustments" % on the budget page), a custom *%*, a *factor ×*, or *none*. The adjusted value is rounded to cents per line. * Amount fields accept calculations, as in Excel: `12*839,67+3292,56` or `=5512*12`. Both `1 234,56` and `1,234.56` are understood. * Months are **split over 12** (December absorbs the rounding cents), **split over chosen months** (tick for example Jan to Apr: the adjusted amount is divided equally over those months and the rest stay at zero), or typed one by one (**monthly amounts**), as the revenue lines are. * Lines can belong to one **site** (Sagres, Quinta, Chiado, Oriente) or to the **group**. Filter any page or report by site. * **New year** can copy every line from an earlier year: last year's adjusted budget becomes the new year's actual and base, which you then replace with real figures. ## Approval workflow Each department has its own workflow, set by an administrator under **Departments**: an ordered list of steps, each approved either by *the department's approvers* or by *selected users* (who can be in any department, e.g. finance). With no steps set, one approval by the department's approvers is needed. `Draft → step 1 → step 2 → … → Approved`. Only drafts can be edited. An editor submits; whoever may approve the current step approves (moving it to the next step) or returns it to draft; an approver of the department or an administrator can reopen an approved budget. Every change (who, when, old and new value) is in **History** on the budget page. ## Reopening an approved budget Anyone can press **Request reopening** on an approved budget and must say why. The department's approvers and the administrators see the request on the budget (and get an email when email is set up), and **Accept and reopen** it (it goes back to draft) or **Decline** it, giving a reason. Approvers can also reopen directly, again with a reason. All requests stay in the budget's **History**. ## Download, change and upload again On a budget page, **Download → Budget file** gives the budget as an editable Excel file (the template filled with its lines). Change it and upload it with **Upload Excel** on the same page (or Budgets → Import Excel). Because that budget already exists, the app asks first. On OK, the current version is kept but renamed with the date and time, for example `Budget 2027 (2026-10-28 23:00)` and `Orcamento_IT_2027_2026-10-28_2300.xlsx`, and locked so it can't be edited; the uploaded file becomes the current budget. Only drafts can be replaced; an approved budget must be reopened first. Archived versions are under **Budgets → Show archived versions**. ## Excel template **Budgets → Excel template** downloads a workbook for a department and year, in Portuguese or English, blank or pre-filled with the lines of an existing budget. It has an instructions sheet and a *Lines* sheet with drop-down lists for category, site and adjustment type. Fill it in and upload it with **Import Excel**; unknown categories are created in the department and anything skipped is listed. ## Email Administrators set up the sending account under **Email → Sending account**: the SMTP server, port and security (STARTTLS on 587 or SSL/TLS on 465), the login, the sender address and name, and an optional signature. With **Save a copy in the Sent folder** on, every message is also stored over IMAP in the mailbox's Sent folder (found automatically, or named by hand), just like a message sent from Outlook. Gmail already keeps that copy for SMTP, so turn the option off there to avoid duplicates. For Gmail and Microsoft 365, use an app password or an account with SMTP sign-in allowed. **Test connection** signs in without sending; **Send test email** sends a short message. Then every user gets **Send by email** on the Reports page: choose recipients, subject, message and the reports to attach (PDF and/or Excel, in the language and site chosen on the page). Passwords are stored encrypted with `SECRET_KEY`. If you change `SECRET_KEY`, enter them again. ## Logs - **Activity** (administrators): who did what and when: sign-ins and failed sign-ins, budgets and lines created, changed, imported, submitted, approved and deleted, reports viewed, downloaded and emailed, and admin changes. Filter by user, area, action, date or text, and export to CSV. - **Email → Mail log** (administrators): every message sent: who sent it, to whom, the subject, message and attachments, whether it was delivered to the server, and whether the copy reached the Sent folder. Also exportable to CSV. Only administrators can delete a budget (in any status), from the budget's page. It asks you to type the year to confirm; the deletion stays in the activity log. ## Backups and restore Backups are written to `BACKUP_DIR` (default `./backups`) at `BACKUP_TIME` every night and once at start. ```sh docker compose exec backup ls /backups docker compose exec backup restore.sh /backups/budget_20270101_023000.sql.gz docker compose restart api ``` Copy the `backups` folder to another machine or a NAS regularly; a backup on the same disk does not protect against losing that disk. ## Updating Your data lives in Docker volumes, not in this folder, so replacing the folder does not touch it. 1. Replace the folder contents with the new version, keeping your `.env` and `backups` folder. 2. Run `docker compose up -d --build`. Before the API applies database changes (migrations), it asks the backup container for a fresh dump (`backups/budget_..._before_upgrade.sql.gz`) and waits for it; if the backup fails, the upgrade does not run. So there is always a backup from just before an upgrade. Upgrading from version 1 moves all existing budgets, categories and users into the **IT** department. Never run `docker compose down -v`: `-v` deletes the data volumes. ## HTTPS (optional) Plain HTTP on port 8080 is the default, which is fine on a trusted network. For HTTPS, set in `.env`: ``` SITE_ADDRESS=budget.yourdomain.local HTTP_PORT=80 HTTPS_PORT=443 COOKIE_SECURE=true ``` and make that name resolve to the server. Caddy issues a certificate from its own local authority for internal names. Browsers will warn until you install its root certificate on each PC: `docker compose cp web:/data/caddy/pki/authorities/local/root.crt caddy-root.crt`. With a public domain pointing at the server, Caddy gets a Let's Encrypt certificate instead. ## Development ```sh cd backend && pip install -r requirements.txt pytest httpx && python -m pytest tests # 44 tests uvicorn app.main:app --reload # API on :8000 (SQLite) cd frontend && npm install && npm run dev # web on :5173 ``` API documentation is at `/api/docs` when the app is running.