summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-18 09:50:29 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-18 09:50:29 +0200
commit6de6ffa3e52a2bec519c2a052d0f26d01e38f167 (patch)
tree4b5625396645262cf2ae8ac74e2161a326f61595
parentec2febbb14c77213e615d935b86651dfd25311bb (diff)
downloadbokf-22f23a26c7ddb9fed58c37d3356ec39f8b6150f5.tar.gz
bokf-22f23a26c7ddb9fed58c37d3356ec39f8b6150f5.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.c76
-rw-r--r--docs/PROTOCOL.md7
-rw-r--r--docs/STATE.md6
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