diff options
| author | Anders Betts <anders.betts@gmail.com> | 2026-09-18 23:41:59 +0200 |
|---|---|---|
| committer | Anders Betts <anders.betts@gmail.com> | 2026-09-18 23:41:59 +0200 |
| commit | 49e44668c14abacef17f0f11be39cbd0faa7ecb9 (patch) | |
| tree | dd8248143def675fd033abf390c06567f78d5c4c /clients/bokftui.c | |
| parent | e9fc91dbc8314a25a0d71b7ee4344d3fc4ccfd5e (diff) | |
| download | bokf-0.1.30.tar.gz bokf-0.1.30.zip | |
bokslut, INK2/SRU export and attachment markersv0.1.30
- bokslut.post: year-end bookings (manual entries, periodiseringsfond,
skatt at a given rate, resultatdisposition) with a dry-run plan through
the normal ledger path; the resultatrapport separates
bokslutsdispositioner and skatt per K2.
- sru.export: INFO.SRU + BLANKETTER.SRU (INK2/INK2R/INK2S) from the
official 2025P4 field tables and the BAS mapping, with manual INK2S
adjustments, submitter defaults and unmapped-account detection; the TUI
writes both files from Rapporter -> Inkomstdeklaration.
- voucher.list carries attachment_count and the voucher list marks
vouchers with underlag with an x column.
- date_prompt restores the cursor state so it stops blinking at the bottom
after the report date prompts.
Diffstat (limited to 'clients/bokftui.c')
| -rw-r--r-- | clients/bokftui.c | 214 |
1 files changed, 144 insertions, 70 deletions
diff --git a/clients/bokftui.c b/clients/bokftui.c index d446974..6c50b1b 100644 --- a/clients/bokftui.c +++ b/clients/bokftui.c @@ -373,7 +373,7 @@ static char *date_prompt(const char *label, const char *def) attroff(A_BOLD); size_t pos = (size_t)-1; int fresh = 1; - curs_set(1); + int prev = curs_set(1); for (;;) { move(y, (int)strlen(label) + 3); clrtoeol(); @@ -384,10 +384,14 @@ static char *date_prompt(const char *label, const char *def) move(y, (int)strlen(label) + 3 + (int)disp_width_n(buf, cp)); refresh(); int ch = ui_getch(); - if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) + if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) { + curs_set(prev == ERR ? 0 : prev); return buf; - if (ch == 27) + } + if (ch == 27) { + curs_set(prev == ERR ? 0 : prev); return NULL; + } date_field_edit(buf, sizeof buf, &pos, ch, &fresh); } } @@ -2599,8 +2603,12 @@ static void vouchers_screen(struct app *a) char *date = jstr_dup(resp, path); snprintf(path, sizeof path, "result.items.%zu.description", i); char *desc = jstr_dup(resp, path); - snprintf(line, sizeof line, "%s %-*s %s", date ? date : "", - idw, idstr[i], desc ? desc : ""); + snprintf(path, sizeof path, "result.items.%zu.attachment_count", + i); + int atts = (int)jint_val(resp, path, 0); + snprintf(line, sizeof line, "%c %s %-*s %s", + atts ? 'x' : ' ', date ? date : "", idw, idstr[i], + desc ? desc : ""); items[i] = xstrdup(line); free(date); free(desc); @@ -3161,6 +3169,47 @@ static void is_print_group(struct buf *t, yyjson_val *res, yyjson_val *prev, *pre = sp; } +static int is_range_has(yyjson_val *res, int lo, int hi) +{ + for (int k = 0; k < 2; k++) { + yyjson_val *sec = yyjson_obj_get(res, IS_KEYS[k]); + yyjson_val *arr = sec ? yyjson_obj_get(sec, "accounts") : NULL; + size_t n = yyjson_arr_size(arr); + for (size_t i = 0; i < n; i++) + if (acct_in(vstr(yyjson_arr_get(arr, i), "account"), lo, hi)) + return 1; + } + return 0; +} + +/* Rows in one account range, printed with the previous-year column; the + signed display sums are added to *sum / *sum_pre. */ +static void is_range_rows(struct buf *t, yyjson_val *res, yyjson_val *prev, + int lo, int hi, int64_t *sum, int64_t *sum_pre) +{ + for (int k = 0; k < 2; k++) { + yyjson_val *sec = yyjson_obj_get(res, IS_KEYS[k]); + yyjson_val *arr = sec ? yyjson_obj_get(sec, "accounts") : NULL; + size_t n = yyjson_arr_size(arr); + for (size_t i = 0; i < n; i++) { + yyjson_val *row = yyjson_arr_get(arr, i); + const char *acc = vstr(row, "account"); + if (!acct_in(acc, lo, hi)) + continue; + int64_t c = k == 0 ? vint(row, "amount_ore") + : -vint(row, "amount_ore"); + int64_t p = is_prev_lookup(prev, acc, k != 0); + char nm[128], a1[48], a2[48]; + pad_label(nm, sizeof nm, vstr(row, "name"), 48); + amt_col(a1, sizeof a1, 15, c); + amt_col(a2, sizeof a2, 15, p); + buf_line(t, " %5s %s %s %s %s\n", acc, nm, a1, a1, a2); + *sum += c; + *sum_pre += p; + } + } +} + static void fmt_resultat(struct buf *t, const struct app *a, yyjson_val *res, yyjson_val *prev) { @@ -3205,45 +3254,14 @@ static void fmt_resultat(struct buf *t, const struct app *a, yyjson_val *res, } is_summary(t, "Summa rörelsekostnader", exp, exp_pre, 0); - /* finansiella poster */ - int64_t fin = 0, fin_pre = 0, booked = 0, booked_pre = 0; - int has_fin = 0, has_booked = 0; - for (int k = 0; k < 2; k++) { - yyjson_val *sec = yyjson_obj_get(res, IS_KEYS[k]); - yyjson_val *arr = sec ? yyjson_obj_get(sec, "accounts") : NULL; - size_t n = yyjson_arr_size(arr); - for (size_t i = 0; i < n; i++) { - yyjson_val *row = yyjson_arr_get(arr, i); - const char *acc = vstr(row, "account"); - if (acct_in(acc, 8000, 8899)) - has_fin = 1; - else if (acct_in(acc, 8900, 8999)) - has_booked = 1; - } - } - if (has_fin) { + int64_t fin = 0, fin_pre = 0, disp = 0, disp_pre = 0; + int64_t tax = 0, tax_pre = 0, booked = 0, booked_pre = 0; + int has_disp = is_range_has(res, 8800, 8899); + int has_tax = is_range_has(res, 8900, 8989); + int has_booked = is_range_has(res, 8999, 8999); + if (is_range_has(res, 8000, 8599)) { buf_line(t, "\nFinansiella poster\n"); - for (int k = 0; k < 2; k++) { - yyjson_val *sec = yyjson_obj_get(res, IS_KEYS[k]); - yyjson_val *arr = sec ? yyjson_obj_get(sec, "accounts") : NULL; - size_t n = yyjson_arr_size(arr); - for (size_t i = 0; i < n; i++) { - yyjson_val *row = yyjson_arr_get(arr, i); - const char *acc = vstr(row, "account"); - if (!acct_in(acc, 8000, 8899)) - continue; - int64_t c = k == 0 ? vint(row, "amount_ore") - : -vint(row, "amount_ore"); - int64_t p = is_prev_lookup(prev, acc, k != 0); - char nm[128], a1[48], a2[48]; - pad_label(nm, sizeof nm, vstr(row, "name"), 48); - amt_col(a1, sizeof a1, 15, c); - amt_col(a2, sizeof a2, 15, p); - buf_line(t, " %5s %s %s %s %s\n", acc, nm, a1, a1, a2); - fin += c; - fin_pre += p; - } - } + is_range_rows(t, res, prev, 8000, 8599, &fin, &fin_pre); is_summary(t, "Summa", fin, fin_pre, 0); } int64_t ror = rev + exp, ror_pre = rev_pre + exp_pre; @@ -3252,41 +3270,34 @@ static void fmt_resultat(struct buf *t, const struct app *a, yyjson_val *res, buf_rule(t, IS_RULE_W); is_summary(t, "Rörelseresultat", ror, ror_pre, 0); is_summary(t, "Resultat efter finansiella poster", after, after_pre, 1); - is_summary(t, "Resultat före skatt", after, after_pre, 1); + if (has_disp) { + buf_line(t, "\nBokslutsdispositioner\n"); + is_range_rows(t, res, prev, 8800, 8899, &disp, &disp_pre); + is_summary(t, "Summa", disp, disp_pre, 0); + } + int64_t before_tax = after + disp, before_tax_pre = after_pre + disp_pre; + is_summary(t, "Resultat före skatt", before_tax, before_tax_pre, 1); + if (has_tax) { + buf_line(t, "\nSkatt\n"); + is_range_rows(t, res, prev, 8900, 8989, &tax, &tax_pre); + is_summary(t, "Summa", tax, tax_pre, 0); + } + int64_t ars = before_tax + tax, ars_pre = before_tax_pre + tax_pre; buf_line(t, "\n"); buf_rule(t, IS_RULE_W); - is_summary(t, "Årets resultat", after, after_pre, 0); + is_summary(t, "Årets resultat", ars, ars_pre, 0); if (has_booked) { - buf_line(t, "Bokfört resultat\n"); - for (int k = 0; k < 2; k++) { - yyjson_val *sec = yyjson_obj_get(res, IS_KEYS[k]); - yyjson_val *arr = sec ? yyjson_obj_get(sec, "accounts") : NULL; - size_t n = yyjson_arr_size(arr); - for (size_t i = 0; i < n; i++) { - yyjson_val *row = yyjson_arr_get(arr, i); - const char *acc = vstr(row, "account"); - if (!acct_in(acc, 8900, 8999)) - continue; - int64_t c = -vint(row, "amount_ore"); - int64_t p = is_prev_lookup(prev, acc, 1); - char nm[128], a1[48], a2[48]; - pad_label(nm, sizeof nm, vstr(row, "name"), 48); - amt_col(a1, sizeof a1, 15, c); - amt_col(a2, sizeof a2, 15, p); - buf_line(t, " %5s %s %s %s %s\n", acc, nm, a1, a1, a2); - booked += c; - booked_pre += p; - } - } + buf_line(t, "\nBokfört resultat\n"); + is_range_rows(t, res, prev, 8999, 8999, &booked, &booked_pre); is_summary(t, "Summa", booked, booked_pre, 0); buf_line(t, "\n"); buf_rule(t, IS_RULE_W); - is_summary(t, "Ej bokfört resultat", after + booked, - after_pre + booked_pre, 0); + is_summary(t, "Ej bokfört resultat", ars + booked, + ars_pre + booked_pre, 0); } else { buf_line(t, "\n"); buf_rule(t, IS_RULE_W); - is_summary(t, "Ej bokfört resultat", after, after_pre, 0); + is_summary(t, "Ej bokfört resultat", ars, ars_pre, 0); } } @@ -3619,6 +3630,64 @@ static void eskd_save(struct app *a, const char *from, const char *to) free(resp); } +static void sru_save(struct app *a) +{ + const char *home = getenv("HOME"); + char def[512]; + snprintf(def, sizeof def, "%s/Downloads", home && *home ? home : "."); + struct stat sb; + if (stat(def, &sb) != 0 || !S_ISDIR(sb.st_mode)) + snprintf(def, sizeof def, "%s", home && *home ? home : "."); + char *dir = prompt("Spara SRU-filer i: ", def, 0); + if (!dir || !*dir) + return; + char full[512]; + if (dir[0] == '~' && (dir[1] == '/' || dir[1] == '\0')) + snprintf(full, sizeof full, "%s%s", home ? home : "", dir + 1); + else + snprintf(full, sizeof full, "%s", dir); + char args[64]; + snprintf(args, sizeof args, "{\"fiscal_year\":%lld}", (long long)a->fy); + char *resp = client_rpc(&a->conn, "sru.export", a->session, a->org, args); + if (!resp || !client_ok(resp)) { + show_error("Kunde inte skapa SRU-filerna", resp); + free(resp); + return; + } + const char *keys[2] = { "result.info.content_base64", + "result.blanketter.content_base64" }; + const char *names[2] = { "INFO.SRU", "BLANKETTER.SRU" }; + char saved[2][600] = { "", "" }; + int ok = 0; + for (int i = 0; i < 2; i++) { + char *b64 = jstr_dup(resp, keys[i]); + unsigned char *data = NULL; + size_t n = 0; + if (!b64 || util_b64_decode(b64, strlen(b64), &data, &n) != 0) { + free(b64); + continue; + } + snprintf(saved[i], sizeof saved[i], "%s/%s", full, names[i]); + FILE *f = fopen(saved[i], "wb"); + if (f && fwrite(data, 1, n, f) == n) { + fclose(f); + ok++; + } else { + if (f) + fclose(f); + message("SRU", "Kunde inte skriva %s: %s", saved[i], + strerror(errno)); + } + free(data); + free(b64); + } + free(resp); + if (ok == 2) + message("SRU", "Sparat:\n%s\n%s\n\nLadda upp båda i Skatteverkets" + " filöverföring.", + saved[0], saved[1]); +} + static void reports_screen(struct app *a) { static const char *const report_items[] = { @@ -3628,16 +3697,21 @@ static void reports_screen(struct app *a) "Huvudbok", "Verifikationslista", "Momsdeklaration", + "Inkomstdeklaration (INK2/SRU)", "Kontolista (alla konton)", }; static int last_sel = 0; for (;;) { if (g_quit) return; - int sel = menu("Rapporter", report_items, 7, 0, &last_sel); + int sel = menu("Rapporter", report_items, 8, 0, &last_sel); if (sel < 0) return; if (sel == 6) { + sru_save(a); + continue; + } + if (sel == 7) { accounts_report(a); continue; } |
