1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
# bokf — project state
Settled decisions live in `docs/DECISIONS.md`; this file is the live snapshot.
Read with `AGENTS.md` (rules) and `docs/TUI-GUIDELINES.md` (UI conventions).
## Status
Working self-hosted bookkeeping system, not production-proven. Backend ledger
core is complete, as are invoicing, the payroll engine and the payroll TUI;
filing/year-end work remains. TUI is usable and exercised by `make test-pty`
(golden screen-text scenarios, now including the payroll run and the
lönebesked); `make test` covers the server/protocol/ledger, the TUI widget
unit tests and the docs consistency check.
## Resume here (2026-09-21)
- **Deployed**: `v0.1.56`, healthy on `nas` (Alpine runtime, static aarch64
binaries cross-compiled on this machine — ~20 s, the host only assembles
the image; image 33.7 MB, no `libssl3`). `v0.1.55` brought the payroll
server waves (schema v11: employees, tax tables, lönebesked) and the
payroll TUI; `v0.1.56` brought the **Bolaget** hub (the Företag section is
gone, the registers live under Bolaget). The live database migrated
v9 → v11 on the v0.1.55 startup with the automatic pre-migration snapshot
in `var/db/backup/`. `main` and the tags are pushed to `nas`.
- **Next tasks (payroll follow-ups)**: fold `payroll.settings_get/set` into
`settings.get/set` (cmd_settings.c was busy during wave 1); settle the
over-80k % rule (SKV 433 leaves it ambiguous — currently a clear
`INVALID_ARGS`); move `invoice.send` to the shared `src/mail.c`. The TUI
has no screen for the payroll account/rate settings yet (they are
defaults and can stay server-side until the fold).
- **Done in the TUI wave**: `clients/screens_payroll.c` with Lönekörningar
(list + Ctrl+N, run screen with F5 preview, Ctrl+Enter post after
confirmation and the action rows Lönebesked, AGI-underlag, Betala skatt &
avgifter), Anställda under Företag and the Skattetabeller
fetch/import/status screen under System. A pty scenario posts a run and
fetches its lönebesked, so the payslip PDF path is exercised end to end.
- **Menu (2026-09-21)**: the dashboard has the sections Bokföring (with
Mallar), Fakturering, Lön, Rapporter & bokslut, System (Skattetabeller,
Revision, Systeminställningar) and Räkenskapsår, plus the standalone
**Bolaget** hub: Företagsuppgifter, Fakturauppgifter, E-post (SMTP),
Styrelseledamöter and the registers Anställda, Kunder and Momsregler.
Inställningar is gone: standardserie, fordrings-/intäktskonto, bankgiro,
vår referens and SMTP live under Bolaget, bilagornas mapp under System.
`--screen settings` remains an alias for Bolaget.
## Open decisions
None open. Completed items that used to be listed here are archived in
`docs/DECISIONS.md`.
## Backlog (prioritized, from COMPLIANCE.md §10 and the audit)
1. ~~eSKD file generation~~ done: `report.vat_eskd`.
2. ~~Bokslut automation~~ done: `bokslut.post` + TUI Bokslut screen.
3. ~~K2 årsredovisning draft and SRU files~~ done: TUI draft + `sru.export`.
4. ~~`audit.verify` voucher chain~~ done (also attachments with `full:true`).
5. ~~`report.general_ledger` / `report.voucher_list`~~ done.
6. ~~`describe` argument schemas~~ done (`CMD_ARGS`).
7. ~~Pre-migration `VACUUM INTO` snapshot~~ done.
8. ~~Docker image + compose~~ done (no registry, no systemd unit).
9. Password change, user disable, TOTP.
10. ~~Bank import/reconciliation (CSV first)~~ phase 1 (SEB CSV + matching,
schema v8) and phase 2 (Ctrl+N prefills the voucher and auto-matches it,
`9b1021e`) done. Phase 3 (`bank_rule.*`) **dropped by the user
2026-09-21**; PSD2 only if it ever hurts.
11a. Imported history years with source P&L closings: decided, no importer
change (see DECISIONS.md).
11. ~~SIE import only into an empty fiscal year~~ chronological multi-year
import works; each year must still target an empty fiscal year.
12. TUI polish: horizontal scrolling in long text fields, bracketed paste.
13. ~~Payroll TUI (wave 3)~~ done (`clients/screens_payroll.c`); the
remaining payroll follow-ups are listed under "Resume here". No
employee is registered yet in the real orgs.
14. Invoice follow-ups when needed: credit notes (`invoice.credit`), per-row
account in the invoice form, kundreskontra view.
15. `make test-pty` speed if it ever exceeds ~60 s: parallelise the
independent scenarios (each has its own rig) and add `--only` symmetry;
measure first.
16. Test fixtures (`t_fresh_org()`) + one test file per domain
(`tests/core_<domain>.c`) so `--only` stops cascading; pilot with one
domain.
Original entries for the struck items are in `docs/DECISIONS.md`.
## Environment / how to run
- **Deployed**: `scripts/deploy.sh` (latest `v0.1.56`, healthy on nas).
Live daemon `tls:bokf.makandra.eu:8788`, token
`~/.config/bokf/migration-token` (scopes `read,write`; owner-only actions
like closing years must be done by the human in the TUI). Git remote
`origin` is `nas:/mnt/data/git-repos/bokf.git` (push `main` and tags).
`sh scripts/install-hooks.sh` once per checkout points `core.hooksPath` at
`.githooks/`, so every push runs `make gate` (clean `-Werror` build in
`build-gate/`, the suite and ASan); it is installed here.
`deploy/Dockerfile.cross` + `deploy/cross-build.sh` produce static aarch64
`bokfd`/`bokfctl` in ~20 s when the host architecture differs; the image is
assembled on the host from `.prebuilt` (see docs/DEPLOY.md).
- **Local test rig** (transient, `/tmp`): daemon
`./build/bokfd --db /tmp/opencode/bokf-local/t.db --socket
/tmp/opencode/bokf-local/sock`, org 1, login `admin`/`testpass123`.
Drive the TUI over a pty with `scripts/tui-sandbox.sh -- ./build/bokftui
--socket /tmp/opencode/bokf-local/sock --org 1 --fy 1 ...` plus a small
driver that feeds keys and an ANSI renderer (recreate if gone; arrows are
`ESC O B/A`, Tab `\t`, `^X` `\x18`, `^Enter` `ESC[27;5;13~`, F5
`ESC[15~`). Never test against the live daemon.
- Demo: db `~/bokf-demo/bokfd.db`, socket `~/bokf-demo/bokfd.sock`,
pid file `~/bokf-demo/bokfd.pid`; login `admin` / `demo1234`.
Start TUI: `cd ~/work/bokf && BOKFD_SOCKET=$HOME/bokf-demo/bokfd.sock \
BOKFD_USER=admin BOKFD_PASSWORD=demo1234 ./build/bokftui`
- Restart daemon: kill the pid file's process, then
`BOKFD_BACKUP_DIR=$HOME/bokf-demo/backup \
BOKFD_EXPORT_DIR=$HOME/bokf-demo/export setsid nohup \
./build/bokfd --db $HOME/bokf-demo/bokfd.db \
--socket $HOME/bokf-demo/bokfd.sock > $HOME/bokf-demo/daemon.log 2>&1 &`
- The user's own early instance was `/tmp/x.db` + `/tmp/bokfd.sock`
(schema v1, old binary) — recreate or migrate it with the current build if
it is still wanted.
- TUI smoke tests: drive over a pty with `script -qec`; function-key escape
sequences are timing-sensitive there (not an app bug). Arrows arrive as
application-mode sequences (`ESC O B` for Down), not `ESC [ B`, because
curses enables the keypad. `Ctrl+N/C/F` are single bytes and reliable.
Always wrap the run in `scripts/tui-sandbox.sh -- ./build/bokftui ...`: it
isolates `XDG_CONFIG_HOME`/`XDG_CACHE_HOME` so a test can never overwrite
the real `~/.config/bokf/tui.conf` or `~/.cache/bokf/tui.log`.
## Known caveats
- Developer tooling: the `g_cmd_<domain>[]` tables in `src/cmd_*.c` carry
declarative argument schemas (`CMD_ARGS`); `describe` emits them and the
dispatcher validates before the handler runs. `make check` (part of
`make test`) fails when a command or error code is missing from
`PROTOCOL.md` (`scripts/check-consistency.sh`) or when the generated
command catalogue is stale (`make gen-protocol`). `make test-asan`/
`test-ubsan` build `test_core` with sanitizers; `make test-pty` runs
`scripts/tui-golden.py` against a throwaway `/tmp` daemon; `make gate`
is the pre-push check (clean `-Werror` build in `build-gate/` + tests +
ASan).
- Never commit unless the human asks.
- SQLite files must not be backed up live with restic; use
`backup.snapshot` (`VACUUM INTO`) and point restic at the snapshots.
- Schema version is 11 (v3 moms rules; v4/v6 year info; v5 org
description/shares + board members; v7 attachments append-only triggers;
v8 bank reconciliation; v9 invoicing + widened `vouchers.source` with a
table rebuild; v10 payroll + `payroll`/`payroll_tax` sources, same
rebuild; v11 `employees.email`); forward migrations are in `db.c`.
## Makandra driftstatus (org 2)
- **Org**: Makandra AB, org 2. Räkenskapsår (id): 2022=3, 2023=4, 2024=5,
2025=6, 2026=7, **2027=2 (öppet, aktuellt)**. Bokslut/AR/deklaration görs
för det år som är valt i sessionen.
- **FK2027**: importerade Kapitas-böcker + 28 bokförda verifikat (V21–V48)
för bank/skatt maj–sep 2026, samt V49 som makulerar en dubblett (V20).
1930 stämmer mot banken utom **CDON 2 409 kr** (väntar på kvittots del
2–4; bokförs när det kommer). 1630 = 40 721 (exakt enligt Skatteverket).
- **Underlag**: 279 attachment i org 2 (alla historikdokument + insamlade
underlag). Bank-/SKV-utdrag ligger i `~/Makandra AB/{bank,skatteverket}`
(Syncthing), källkorpus i `~/Downloads/Makandra AB-…/Bokföring/`.
- **Stängning**: 2022–2026 ska stängas av ägaren via **Räkenskapsår** i
TUI:n; låt FK2027 vara öppen till nästa bokslut.
- **Deklaration**: FK2026 är deklarerad av revisorn. FK2027 deklareras
våren 2027 (INK2/SRU via Bokslutshubben → Inkomstdeklaration).
- **Årshäftet**: fylls i Bokslutshubben (händelser, stämma, utdelning +
datum, medelantal, noter). OBS: `dividend_ore` för FK2027 kan vara ett
testvärde (10 000) — kontrollera före AR/deklaration.
- **Beslut/regler från bokföringsarbetet**: inga bokföringar utan
godkännande; låsta år förblir låsta (rättelser görs i aktuellt år); SIE-
importören ändras inte och importerad data "manipuleras" inte;
historikårens P&L nettar noll pga källsystemets stängningar (AR hoppar
över "Stäng"-verifikat i flerårsöversikten); utdelning bokförs vid stämman
med mallen **Utdelning** (D 2099/K 2898); pappersoriginal finns i fysisk
pärm (får refereras i efterhand, även i stängda år).
- **Lön (2026-09-21)**: bara ägaren är anställd, men schema och kommandon är
fleranvändarklara. Lönemotorn (schema v11: anställda, skattetabeller,
lönekörningar, lönebesked + mejl) och TUI-sektionen Lön är klara, men
**ingen anställd är registrerad än** (personnummer, lön, skattetabell och
kolumn fylls i under Register → Anställda). Konton enligt historiken:
7210/7510/2710/2731/1630. Skattetabellerna hämtas med en knapp från
Skatteverket (varning när året är gammalt), personnumret är krypterat i
databasen. Ingen semesteruppbokning; arbetsgivardeklarationen (AGI) görs
manuellt med underlaget från `payroll.agi`.
|