diff options
| author | Anders Betts <anders.betts@gmail.com> | 2026-09-18 09:50:29 +0200 |
|---|---|---|
| committer | Anders Betts <anders.betts@gmail.com> | 2026-09-18 09:50:29 +0200 |
| commit | 6de6ffa3e52a2bec519c2a052d0f26d01e38f167 (patch) | |
| tree | 4b5625396645262cf2ae8ac74e2161a326f61595 | |
| parent | ec2febbb14c77213e615d935b86651dfd25311bb (diff) | |
| download | bokf-6de6ffa3e52a2bec519c2a052d0f26d01e38f167.tar.gz bokf-6de6ffa3e52a2bec519c2a052d0f26d01e38f167.zip | |
bokftui: IB view shows effective opening balances, edits post deltas to themv0.1.11
Was listing the IB voucher's rows (the reconciliation deltas) and
computing edits against those, which mismatched reports on years with
carry-forward and could not set a target balance. Also guard the editor
against more than 63 rows.
| -rw-r--r-- | clients/bokftui.c | 76 | ||||
| -rw-r--r-- | docs/PROTOCOL.md | 7 | ||||
| -rw-r--r-- | docs/STATE.md | 6 |
3 files changed, 33 insertions, 56 deletions
diff --git a/clients/bokftui.c b/clients/bokftui.c index 15ca335..5daa606 100644 --- a/clients/bokftui.c +++ b/clients/bokftui.c @@ -3171,6 +3171,8 @@ static void ibrow_normalize(struct tui_ibrow *rows, int *nrows, int *field) } /* Loads the net IB per account for the active fiscal year. */ +/* Effective opening balances of the selected year: the year's IB vouchers + plus all earlier movements, exactly what the reports show. */ static int ib_load(struct app *a, char ***out_acc, int64_t **out_amt, int *out_n) { @@ -3178,67 +3180,33 @@ static int ib_load(struct app *a, char ***out_acc, int64_t **out_amt, *out_amt = NULL; *out_n = 0; char args[128]; - snprintf(args, sizeof args, "{\"fiscal_year\":%lld,\"series\":\"IB\"," - "\"limit\":200}", + snprintf(args, sizeof args, + "{\"fiscal_year\":%lld,\"include_zero\":true}", (long long)a->fy); - char *resp = - client_rpc(&a->conn, "voucher.list", a->session, a->org, args); + char *resp = client_rpc(&a->conn, "report.trial_balance", a->session, + a->org, args); if (!resp || !client_ok(resp)) { show_error("Ingående balans", resp); free(resp); return -1; } - size_t n = jarr_size(resp, "result.items"); - size_t cap = n ? n : 1; - char **accs = xcalloc(cap, sizeof(char *)); - int64_t *amts = xcalloc(cap, sizeof(int64_t)); + size_t n = jarr_size(resp, "result.accounts"); + char **accs = xcalloc(n ? n : 1, sizeof(char *)); + int64_t *amts = xcalloc(n ? n : 1, sizeof(int64_t)); int count = 0; for (size_t i = 0; i < n; i++) { char path[64]; - snprintf(path, sizeof path, "result.items.%zu.id", i); - int64_t id = jint_val(resp, path, 0); - if (id <= 0) + snprintf(path, sizeof path, "result.accounts.%zu.ib_ore", i); + int64_t ib = jint_val(resp, path, 0); + if (ib == 0) continue; - char vargs[64]; - snprintf(vargs, sizeof vargs, "{\"id\":%lld}", (long long)id); - char *v = client_rpc(&a->conn, "voucher.get", a->session, a->org, vargs); - if (!v || !client_ok(v)) { - free(v); + snprintf(path, sizeof path, "result.accounts.%zu.account", i); + char *acc = jstr_dup(resp, path); + if (!acc) continue; - } - size_t rn = jarr_size(v, "result.rows"); - for (size_t k = 0; k < rn; k++) { - char p[64]; - snprintf(p, sizeof p, "result.rows.%zu.account", k); - char *acc = jstr_dup(v, p); - snprintf(p, sizeof p, "result.rows.%zu.debit_ore", k); - int64_t d = jint_val(v, p, 0); - snprintf(p, sizeof p, "result.rows.%zu.credit_ore", k); - int64_t c = jint_val(v, p, 0); - if (acc) { - int found = -1; - for (int j = 0; j < count; j++) - if (strcmp(accs[j], acc) == 0) { - found = j; - break; - } - if (found >= 0) { - amts[found] += d - c; - } else { - if ((size_t)count == cap) { - cap *= 2; - accs = xrealloc(accs, cap * sizeof(char *)); - amts = xrealloc(amts, cap * sizeof(int64_t)); - } - accs[count] = acc; - amts[count] = d - c; - count++; - } - if (found >= 0) - free(acc); - } - } - free(v); + accs[count] = acc; + amts[count] = ib; + count++; } free(resp); *out_acc = accs; @@ -3259,6 +3227,11 @@ static int64_t ib_old_lookup(char **accs, int64_t *amts, int n, static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold) { static struct tui_ibrow rows[64]; + if (nold > 63) { + message("Ingående balans", + "För många konton (%d) för att redigera här.", nold); + return 0; + } memset(rows, 0, sizeof rows); for (int i = 0; i < nold && i < 63; i++) { snprintf(rows[i].account, sizeof rows[i].account, "%s", old_acc[i]); @@ -3565,7 +3538,8 @@ static void ib_screen(struct app *a) top = 0; frame("Ingående balans"); attron(A_BOLD); - mvprintw(2, 2, "Räkenskapsår %s serie IB", a->fy_label); + mvprintw(2, 2, "Räkenskapsår %s ingående balans (effektiv)", + a->fy_label); mvaddstr(4, 2, "Konto"); mvaddstr(4, 9, "Namn"); mvaddstr(4, 62, "Belopp"); diff --git a/docs/PROTOCOL.md b/docs/PROTOCOL.md index 009b14e..f282691 100644 --- a/docs/PROTOCOL.md +++ b/docs/PROTOCOL.md @@ -404,9 +404,10 @@ commands. Implemented screens (0.1.0-dev): - **Nytt verifikat** — row editor with live balance display, F5 dry-run validation and F9 posting; one `client_ref` per form makes retries safe. F4 applies a konteringsmall (prompts for template and `x`). -- **Ingående balans** — the series `IB` voucher for the selected fiscal year; - enter accounts with signed amounts (positive debit, negative credit), the - editor posts deltas so existing entries are never edited. +- **Ingående balans** — the effective opening balances of the selected + fiscal year (carry-forward plus any `IB` vouchers, as the reports compute + them); enter accounts with signed amounts (positive debit, negative + credit); the editor posts `IB` deltas so nothing is ever edited. - **Välj organisation att representera** — login step two: pick which org the session works in. `--org ID` skips the picker for scripts. - **Byt räkenskapsår** (dashboard) — pick from the org's fiscal years; shown diff --git a/docs/STATE.md b/docs/STATE.md index f130f14..3cbe448 100644 --- a/docs/STATE.md +++ b/docs/STATE.md @@ -41,8 +41,10 @@ server/protocol/ledger only. instead of delete. 9. **Ingående balans**: one series `IB` voucher per fiscal year (dated at year start). Reports treat series IB as IB, not period movement; SIE - export/import round-trips without double counting; TUI editor posts only - deltas so nothing is ever edited. + export/import round-trips without double counting; TUI editor shows the + year's *effective* opening balances (carry-forward plus IB vouchers) and + posts deltas, so nothing is ever edited and a target balance can be + entered directly. 10. **Attachments**: content stored in the DB (BLOB), immutable, linked via append-only `voucher_attachments`. Default limit 10 MiB (`max_attachment_bytes`); the socket line limit is derived from it |
