aboutsummaryrefslogtreecommitdiff
path: root/clients/bokftui.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/bokftui.c')
-rw-r--r--clients/bokftui.c442
1 files changed, 242 insertions, 200 deletions
diff --git a/clients/bokftui.c b/clients/bokftui.c
index e94faeb..64bed07 100644
--- a/clients/bokftui.c
+++ b/clients/bokftui.c
@@ -25,6 +25,12 @@
#define KEY_CTRL_X 0x18
/* ^Enter saves/posts the form; sent via xterm modifyOtherKeys */
#define KEY_CTRL_ENTER (KEY_MAX + 1)
+/* Pager markup: headings, dimmed derived lines and full-width rules. */
+#define MK_HEAD "\001"
+#define MK_DIM "\002"
+#define MK_RULE "\003"
+/* Menu item prefix for a non-selectable section header. */
+#define MK_SECTION MK_HEAD
/* ^C quits the application; Esc is navigation only */
static int g_quit = 0;
/* ^R re-execs the binary, keeping session, org, year and screen */
@@ -1916,13 +1922,14 @@ static void buf_line(struct buf *b, const char *fmt, ...)
static void buf_rule(struct buf *b, int width)
{
char line[128];
- if (width > (int)sizeof line - 2)
- width = (int)sizeof line - 2;
+ if (width > (int)sizeof line - 3)
+ width = (int)sizeof line - 3;
if (width < 1)
return;
- memset(line, '-', (size_t)width);
- line[width] = '\n';
- buf_append(b, line, (size_t)width + 1);
+ line[0] = MK_RULE[0];
+ memset(line + 1, '-', (size_t)width);
+ line[width + 1] = '\n';
+ buf_append(b, line, (size_t)width + 2);
}
/* prefix (8) + label (48) + amount columns */
@@ -1935,9 +1942,9 @@ static void fmt_head(struct buf *t, const struct app *a, const char *title,
yyjson_val *fy = yyjson_obj_get(res, "fiscal_year");
const char *start = vstr(fy, "start_date");
const char *end = vstr(fy, "end_date");
- buf_line(t, "%s\n%s\n\n", title, a->org_name);
- buf_line(t, "Räkenskapsår: %s - %s\n", start, end);
- buf_line(t, "Period: %s - %s\n\n", start, end);
+ buf_line(t, MK_HEAD "%s\n%s\n\n", title, a->org_name);
+ buf_line(t, MK_DIM "Räkenskapsår: %s - %s\n", start, end);
+ buf_line(t, MK_DIM "Period: %s - %s\n\n", start, end);
}
static int acct_in(const char *number, int lo, int hi)
@@ -2018,7 +2025,7 @@ static void bs_sum_line(struct buf *t, const char *label,
tui_amt_col(a2, sizeof a2, 14, s->ib);
tui_amt_col(a3, sizeof a3, 14, s->per);
tui_amt_col(a4, sizeof a4, 14, s->ub);
- buf_line(t, " %5s %s %s %s %s %s\n", "", lbl, a1, a2, a3, a4);
+ buf_line(t, MK_DIM " %5s %s %s %s %s %s\n", "", lbl, a1, a2, a3, a4);
}
static int bs_group(struct buf *t, yyjson_val *res, const char *title,
@@ -2027,7 +2034,7 @@ static int bs_group(struct buf *t, yyjson_val *res, const char *title,
struct bs_sum probe = { 0, 0, 0 };
if (!bs_rows(NULL, res, lo, hi, &probe))
return 0;
- buf_line(t, "%s\n", title);
+ buf_line(t, MK_HEAD "%s\n", title);
struct bs_sum s = { 0, 0, 0 };
bs_rows(t, res, lo, hi, &s);
bs_sum_line(t, sumlabel, &s);
@@ -2056,12 +2063,12 @@ static void fmt_balans(struct buf *t, const struct app *a, yyjson_val *res)
fmt_head(t, a, "Balansrapport", res);
struct bs_sum anl = { 0, 0, 0 }, oms = { 0, 0, 0 };
struct bs_sum eget = { 0, 0, 0 }, skuld = { 0, 0, 0 };
- buf_line(t, "TILLGÅNGAR\n");
+ buf_line(t, MK_HEAD "TILLGÅNGAR\n");
bs_col_head(t);
- buf_line(t, "\nAnläggningstillgångar\n");
+ buf_line(t, "\n" MK_HEAD "Anläggningstillgångar\n");
bs_rows(t, res, 1000, 1399, &anl);
bs_sum_line(t, "Summa anläggningstillgångar", &anl);
- buf_line(t, "\nOmsättningstillgångar\n");
+ buf_line(t, "\n" MK_HEAD "Omsättningstillgångar\n");
int printed = 0;
for (size_t i = 0; i < sizeof OMS / sizeof OMS[0]; i++) {
struct bs_sum probe = { 0, 0, 0 };
@@ -2078,9 +2085,9 @@ static void fmt_balans(struct buf *t, const struct app *a, yyjson_val *res)
buf_rule(t, BS_RULE_W);
bs_sum_line(t, "Summa tillgångar", &as);
- buf_line(t, "\nEGET KAPITAL OCH SKULDER\n");
+ buf_line(t, "\n" MK_HEAD "EGET KAPITAL OCH SKULDER\n");
bs_col_head(t);
- buf_line(t, "\nEget kapital\n");
+ buf_line(t, "\n" MK_HEAD "Eget kapital\n");
bs_group(t, res, "Bundet eget kapital", "Summa bundet eget kapital", 2000,
2090, &eget);
buf_line(t, "\n");
@@ -2106,7 +2113,7 @@ static void fmt_balans(struct buf *t, const struct app *a, yyjson_val *res)
tui_amt_col(a1, sizeof a1, 14, result);
buf_line(t, "\n");
buf_rule(t, BS_RULE_W);
- buf_line(t, " %5s %s %s %s %s %s\n", "", lbl, zero, zero, a1, a1);
+ buf_line(t, MK_DIM " %5s %s %s %s %s %s\n", "", lbl, zero, zero, a1, a1);
}
/* ---------------- resultaträkning ---------------- */
@@ -2175,7 +2182,7 @@ static void is_summary(struct buf *t, const char *label, int64_t cur,
tui_pad_label(lbl, sizeof lbl, label, 48);
tui_amt_col(a1, sizeof a1, 15, cur);
tui_amt_col(a2, sizeof a2, 15, prev);
- buf_line(t, " %5s %s %s %s %s\n", "", lbl, a1, a1, a2);
+ buf_line(t, MK_DIM " %5s %s %s %s %s\n", "", lbl, a1, a1, a2);
}
static void is_print_group(struct buf *t, yyjson_val *res, yyjson_val *prev,
@@ -2265,22 +2272,23 @@ static void fmt_resultat(struct buf *t, const struct app *a, yyjson_val *res,
is_summary(t, "Summa rörelseintäkter, lagerförändringar m.m.",
rev, rev_pre, 0);
buf_line(t, "\n");
- buf_line(t, "%s\n", gr->section);
+ buf_line(t, MK_HEAD "%s\n", gr->section);
} else {
char lbl[80], h1[32], h2[32], h3[32];
tui_pad_label(lbl, sizeof lbl, gr->section, 48);
tui_pad_hdr(h1, sizeof h1, 15, "Period");
tui_pad_hdr(h2, sizeof h2, 15, "Ackumulerat");
tui_pad_hdr(h3, sizeof h3, 15, "Föreg. per.");
- buf_line(t, " %5s %s %s %s %s\n", "", lbl, h1, h2, h3);
+ buf_line(t, MK_HEAD " %5s %s %s %s %s\n", "", lbl, h1, h2,
+ h3);
buf_rule(t, IS_RULE_W);
buf_line(t, "\n");
- buf_line(t, "%s\n", gr->title);
+ buf_line(t, MK_HEAD "%s\n", gr->title);
}
sect = gr->section;
} else {
buf_line(t, "\n");
- buf_line(t, "%s\n", gr->title);
+ buf_line(t, MK_HEAD "%s\n", gr->title);
}
int64_t sc = 0, sp = 0;
is_print_group(t, res, prev, gr, &sc, &sp);
@@ -2300,7 +2308,7 @@ static void fmt_resultat(struct buf *t, const struct app *a, yyjson_val *res,
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");
+ buf_line(t, "\n" MK_HEAD "Finansiella poster\n");
is_range_rows(t, res, prev, 8000, 8599, &fin, &fin_pre);
is_summary(t, "Summa", fin, fin_pre, 0);
}
@@ -2311,14 +2319,14 @@ static void fmt_resultat(struct buf *t, const struct app *a, yyjson_val *res,
is_summary(t, "Rörelseresultat", ror, ror_pre, 0);
is_summary(t, "Resultat efter finansiella poster", after, after_pre, 1);
if (has_disp) {
- buf_line(t, "\nBokslutsdispositioner\n");
+ buf_line(t, "\n" MK_HEAD "Bokslutsdispositioner\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");
+ buf_line(t, "\n" MK_HEAD "Skatt\n");
is_range_rows(t, res, prev, 8900, 8989, &tax, &tax_pre);
is_summary(t, "Summa", tax, tax_pre, 0);
}
@@ -2327,7 +2335,7 @@ static void fmt_resultat(struct buf *t, const struct app *a, yyjson_val *res,
buf_rule(t, IS_RULE_W);
is_summary(t, "Årets resultat", ars, ars_pre, 0);
if (has_booked) {
- buf_line(t, "\nBokfört resultat\n");
+ buf_line(t, "\n" MK_HEAD "Bokfö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");
@@ -2353,7 +2361,8 @@ static void fmt_saldobalans(struct buf *t, const struct app *a,
tui_pad_hdr(h2, sizeof h2, 14, "Debet");
tui_pad_hdr(h3, sizeof h3, 14, "Kredit");
tui_pad_hdr(h4, sizeof h4, 14, "Utgående");
- buf_line(t, " %5s %s %s %s %s %s\n", "Konto", nmh, h1, h2, h3, h4);
+ buf_line(t, MK_HEAD " %5s %s %s %s %s %s\n", "Konto", nmh, h1, h2, h3,
+ h4);
buf_rule(t, BS_RULE_W);
yyjson_val *arr = yyjson_obj_get(res, "accounts");
size_t n = yyjson_arr_size(arr);
@@ -2377,7 +2386,7 @@ static void fmt_saldobalans(struct buf *t, const struct app *a,
tui_amt_col(a4, sizeof a4, 14, vint(tot, "ub_ore"));
buf_line(t, "\n");
buf_rule(t, BS_RULE_W);
- buf_line(t, " %5s %s %s %s %s %s\n", "", lbl, a1, a2, a3, a4);
+ buf_line(t, MK_DIM " %5s %s %s %s %s %s\n", "", lbl, a1, a2, a3, a4);
}
/* ---------------- momsdeklaration ---------------- */
@@ -2444,9 +2453,9 @@ static const char *vat_section_title(const char *s)
static void fmt_moms(struct buf *t, const struct app *a, yyjson_val *res)
{
- buf_line(t, "Momsrapport\n\n%s\n\n", a->org_name);
- buf_line(t, "Räkenskapsår: %s - %s\n", a->fy_start, a->fy_end);
- buf_line(t, "Period: %s - %s\n", vstr(res, "from"),
+ buf_line(t, MK_HEAD "Momsrapport\n\n%s\n\n", a->org_name);
+ buf_line(t, MK_DIM "Räkenskapsår: %s - %s\n", a->fy_start, a->fy_end);
+ buf_line(t, MK_DIM "Period: %s - %s\n", vstr(res, "from"),
vstr(res, "to"));
const char *sect = NULL;
yyjson_val *boxes = yyjson_obj_get(res, "boxes");
@@ -2457,7 +2466,7 @@ static void fmt_moms(struct buf *t, const struct app *a, yyjson_val *res)
continue; /* last */
if (!sect || strcmp(sect, vr->section) != 0) {
sect = vr->section;
- buf_line(t, "\n%s %s\n", vr->section,
+ buf_line(t, "\n" MK_HEAD "%s %s\n", vr->section,
vat_section_title(vr->section));
}
int64_t ore = 0;
@@ -2483,14 +2492,14 @@ static void fmt_moms(struct buf *t, const struct app *a, yyjson_val *res)
break;
}
}
- buf_line(t, "\nG. %s\n", vat_section_title("G."));
+ buf_line(t, "\n" MK_HEAD "G. %s\n", vat_section_title("G."));
buf_rule(t, 87);
char amt49[48], lbl49[192];
kr_whole(ore49, amt49, sizeof amt49);
snprintf(lbl49, sizeof lbl49, "%s",
"Moms att betala (+) eller att få tillbaka (-)");
pad_min(lbl49, sizeof lbl49, 66);
- buf_line(t, "49. %s %14s\n", lbl49, amt49);
+ buf_line(t, MK_DIM "49. %s %14s\n", lbl49, amt49);
}
static int64_t prev_fy_id(struct app *a)
@@ -2529,9 +2538,9 @@ static void fmt_huvudbok(struct buf *t, const struct app *a, yyjson_val *res)
fmt_head(t, a, "Huvudbok", res);
yyjson_val *lv = yyjson_obj_get(res, "last_voucher");
if (lv)
- buf_line(t, "Senaste ver.: %s%lld\n\n", vstr(lv, "series"),
+ buf_line(t, MK_DIM "Senaste ver.: %s%lld\n\n", vstr(lv, "series"),
(long long)vint(lv, "number"));
- buf_line(t, "%-8s %-58s %14s %14s %16s\n", "Konto", "", "Debet",
+ buf_line(t, MK_HEAD "%-8s %-58s %14s %14s %16s\n", "Konto", "", "Debet",
"Kredit", "Saldo");
buf_rule(t, 114);
yyjson_val *accs = yyjson_obj_get(res, "accounts");
@@ -2541,14 +2550,15 @@ static void fmt_huvudbok(struct buf *t, const struct app *a, yyjson_val *res)
int64_t ib = vint(ao, "ib_ore");
int64_t d = vint(ao, "debit_ore");
int64_t c = vint(ao, "credit_ore");
- buf_line(t, "\n%s %s\n\n", vstr(ao, "account"), vstr(ao, "name"));
+ buf_line(t, "\n" MK_HEAD "%s %s\n\n", vstr(ao, "account"),
+ vstr(ao, "name"));
char a1[48];
tui_amt_col(a1, sizeof a1, 16, ib);
char lbl[96];
tui_pad_label(lbl, sizeof lbl, "Ingående balans:", 59);
- buf_line(t, " %s %14s %14s %16s\n", lbl, "", "", a1);
+ buf_line(t, MK_DIM " %s %14s %14s %16s\n", lbl, "", "", a1);
tui_pad_label(lbl, sizeof lbl, "Ingående saldo:", 59);
- buf_line(t, " %s %14s %14s %16s\n", lbl, "", "", a1);
+ buf_line(t, MK_DIM " %s %14s %14s %16s\n", lbl, "", "", a1);
buf_line(t, "\n");
yyjson_val *rows = yyjson_obj_get(ao, "rows");
size_t nr = yyjson_arr_size(rows);
@@ -2574,10 +2584,10 @@ static void fmt_huvudbok(struct buf *t, const struct app *a, yyjson_val *res)
tui_amt_col(s2, sizeof s2, 14, c);
tui_amt_col(s3, sizeof s3, 16, d - c);
tui_pad_label(lbl, sizeof lbl, "Omslutning:", 59);
- buf_line(t, " %s %14s %14s %16s\n", lbl, s1, s2, s3);
+ buf_line(t, MK_DIM " %s %14s %14s %16s\n", lbl, s1, s2, s3);
tui_amt_col(s3, sizeof s3, 16, vint(ao, "ub_ore"));
tui_pad_label(lbl, sizeof lbl, "Utgående saldo:", 59);
- buf_line(t, " %s %14s %14s %16s\n", lbl, s1, s2, s3);
+ buf_line(t, MK_DIM " %s %14s %14s %16s\n", lbl, s1, s2, s3);
}
}
@@ -2589,12 +2599,12 @@ static void fmt_verifikationslista(struct buf *t, const struct app *a,
fmt_head(t, a, "Verifikationslista", res);
yyjson_val *lv = yyjson_obj_get(res, "last_voucher");
if (lv)
- buf_line(t, "Senaste ver.: %s%lld\n\n", vstr(lv, "series"),
+ buf_line(t, MK_DIM "Senaste ver.: %s%lld\n\n", vstr(lv, "series"),
(long long)vint(lv, "number"));
char nmh[64];
tui_pad_label(nmh, sizeof nmh, "Benämning", 48);
- buf_line(t, "%-8s %-11s %s %14s %14s\n", "Ver.", "Datum/Konto", nmh,
- "Debet", "Kredit");
+ buf_line(t, MK_HEAD "%-8s %-11s %s %14s %14s\n", "Ver.", "Datum/Konto",
+ nmh, "Debet", "Kredit");
buf_rule(t, 98);
yyjson_val *vs = yyjson_obj_get(res, "vouchers");
size_t n = yyjson_arr_size(vs);
@@ -2603,7 +2613,7 @@ static void fmt_verifikationslista(struct buf *t, const struct app *a,
char ver[32];
snprintf(ver, sizeof ver, "%s%lld", vstr(vo, "series"),
(long long)vint(vo, "number"));
- buf_line(t, "%-8s %-11s %s\n", ver, vstr(vo, "date"),
+ buf_line(t, MK_HEAD "%-8s %-11s %s\n", ver, vstr(vo, "date"),
vstr(vo, "description"));
yyjson_val *rows = yyjson_obj_get(vo, "rows");
size_t nr = yyjson_arr_size(rows);
@@ -2623,7 +2633,7 @@ static void fmt_verifikationslista(struct buf *t, const struct app *a,
char s1[48], s2[48];
tui_amt_col(s1, sizeof s1, 14, vint(tot, "debit_ore"));
tui_amt_col(s2, sizeof s2, 14, vint(tot, "credit_ore"));
- buf_line(t, "\n%-69s %14s %14s\n", "Omslutning:", s1, s2);
+ buf_line(t, "\n" MK_DIM "%-69s %14s %14s\n", "Omslutning:", s1, s2);
}
static void eskd_save(struct app *a, const char *from, const char *to)
@@ -2856,23 +2866,23 @@ static void sru_rows(struct buf *t, yyjson_val *arr)
static void fmt_ink2(struct buf *t, const struct app *a, yyjson_val *res)
{
- buf_line(t, "Inkomstdeklaration 2 (INK2)\n%s\n\n", a->org_name);
- buf_line(t, "Räkenskapsår: %s - %s\n", vstr(res, "from"),
+ buf_line(t, MK_HEAD "Inkomstdeklaration 2 (INK2)\n%s\n\n", a->org_name);
+ buf_line(t, MK_DIM "Räkenskapsår: %s - %s\n", vstr(res, "from"),
vstr(res, "to"));
- buf_line(t, "Blankett: %s\n", vstr(res, "period"));
- buf_line(t, "\nINK2 - huvudblankett\n");
+ buf_line(t, MK_DIM "Blankett: %s\n", vstr(res, "period"));
+ buf_line(t, "\n" MK_HEAD "INK2 - huvudblankett\n");
buf_rule(t, 81);
sru_rows(t, yyjson_obj_get(res, "ink2"));
- buf_line(t, "\nINK2R - räkenskapsschema\n");
+ buf_line(t, "\n" MK_HEAD "INK2R - räkenskapsschema\n");
buf_rule(t, 81);
sru_rows(t, yyjson_obj_get(res, "ink2r"));
- buf_line(t, "\nINK2S - skattemässiga justeringar\n");
+ buf_line(t, "\n" MK_HEAD "INK2S - skattemässiga justeringar\n");
buf_rule(t, 81);
sru_rows(t, yyjson_obj_get(res, "ink2s"));
yyjson_val *un = yyjson_obj_get(res, "unmapped");
size_t nu = yyjson_arr_size(un);
if (nu) {
- buf_line(t, "\nKonton utan SRU-mappning (måste åtgärdas):\n");
+ buf_line(t, "\n" MK_HEAD "Konton utan SRU-mappning (måste åtgärdas):\n");
for (size_t i = 0; i < nu; i++) {
yyjson_val *row = yyjson_arr_get(un, i);
char amt[48];
@@ -4126,87 +4136,6 @@ static void board_screen(struct app *a)
}
}
-/* "Information om året": the per-year årsredovisning details. New years
- inherit the stable fields, so only what changed needs editing. */
-static void yearinfo_screen(struct app *a)
-{
- static const char *const labels[6] = {
- "Väsentliga händelser",
- "Årsstämma (YYYY-MM-DD)",
- "Föreslagen utdelning (kr)",
- "Utdelningens datum (YYYY-MM-DD)",
- "Medelantal anställda",
- "Övriga noter (t.ex. revisor)",
- };
- static const char *const keys[6] = { "events", "agm_date", "dividend_ore",
- "dividend_date", "employees",
- "notes" };
- const int nf = 6;
- int can_edit = strcmp(a->role, "owner") == 0 ||
- strcmp(a->role, "bookkeeper") == 0;
- for (;;) {
- if (g_quit)
- return;
- char args[48];
- snprintf(args, sizeof args, "{\"id\":%lld}", (long long)a->fy);
- char *resp = client_rpc(&a->conn, "fiscal_year.get", a->session,
- a->org, args);
- if (!resp || !client_ok(resp)) {
- show_error("Information om året", resp);
- free(resp);
- return;
- }
- char *vals[6];
- struct tui_form_field ff[6];
- int64_t div_ore = jint_val(resp, "result.dividend_ore", 0);
- memset(ff, 0, sizeof ff);
- for (int i = 0; i < nf; i++) {
- vals[i] = xcalloc(512, 1);
- if (i != 2) {
- char path[64], *v;
- snprintf(path, sizeof path, "result.%s", keys[i]);
- v = jstr_dup(resp, path);
- snprintf(vals[i], 512, "%s", v ? v : "");
- free(v);
- }
- ff[i].label = labels[i];
- ff[i].value = vals[i];
- ff[i].cap = 512;
- }
- free(resp);
- ff[0].kind = TUI_F_TEXT;
- ff[1].kind = TUI_F_DATE;
- ff[2].kind = TUI_F_AMOUNT;
- ff[2].ore = &div_ore;
- ff[3].kind = TUI_F_DATE;
- ff[4].kind = TUI_F_TEXT;
- ff[5].kind = TUI_F_TEXT;
- int r = tui_form_run("Information om året", ff, nf, can_edit);
- if (r >= 0) {
- yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
- yyjson_mut_val *o = yyjson_mut_obj(d);
- yyjson_mut_doc_set_root(d, o);
- yyjson_mut_obj_add_int(d, o, "id", a->fy);
- if (r == 2)
- yyjson_mut_obj_add_int(d, o, "dividend_ore", div_ore);
- else
- yyjson_mut_obj_add_strcpy(d, o, keys[r], vals[r]);
- char *uargs = yyjson_mut_write(d, 0, NULL);
- yyjson_mut_doc_free(d);
- char *rr = client_rpc(&a->conn, "fiscal_year.update", a->session,
- a->org, uargs);
- free(uargs);
- if (!rr || !client_ok(rr))
- show_error("Kunde inte spara", rr);
- free(rr);
- }
- for (int i = 0; i < nf; i++)
- free(vals[i]);
- if (r == TUI_FORM_BACK)
- return;
- }
-}
-
static void company_screen(struct app *a)
{
const int nf = (int)(sizeof COMPANY_FIELDS / sizeof COMPANY_FIELDS[0]);
@@ -4441,7 +4370,7 @@ static void fmt_bokslut_plan(struct buf *t, yyjson_val *res)
{
yyjson_val *fy = yyjson_obj_get(res, "fiscal_year");
if (fy)
- buf_line(t, "Bokslutsplan %s %s - %s\n", vstr(fy, "label"),
+ buf_line(t, MK_HEAD "Bokslutsplan %s %s - %s\n", vstr(fy, "label"),
vstr(fy, "start_date"), vstr(fy, "end_date"));
yyjson_val *vs = yyjson_obj_get(res, "vouchers");
size_t n = yyjson_arr_size(vs);
@@ -4463,7 +4392,8 @@ static void fmt_bokslut_plan(struct buf *t, yyjson_val *res)
num = pnum + 1;
}
pnum = num;
- buf_line(t, "\n%s%lld %s\n", pser, num, vstr(v, "description"));
+ buf_line(t, "\n" MK_HEAD "%s%lld %s\n", pser, num,
+ vstr(v, "description"));
yyjson_val *rows = yyjson_obj_get(v, "rows");
size_t nr = yyjson_arr_size(rows);
for (size_t k = 0; k < nr; k++) {
@@ -4544,23 +4474,92 @@ static void bokslut_plan(struct app *a, const char *fond, const char *rate,
free(presp);
}
+/* Bokslut: the per-year årsredovisning details (fields 0-5, saved one by
+ one with fiscal_year.update) plus the local periodiseringsfond and tax
+ rate inputs. New years inherit the stable fields. */
static void bokslut_screen(struct app *a)
{
+ static const char *const labels[6] = {
+ "Väsentliga händelser",
+ "Årsstämma (YYYY-MM-DD)",
+ "Föreslagen utdelning (kr)",
+ "Utdelningens datum (YYYY-MM-DD)",
+ "Medelantal anställda",
+ "Övriga noter (t.ex. revisor)",
+ };
+ static const char *const keys[6] = { "events", "agm_date", "dividend_ore",
+ "dividend_date", "employees",
+ "notes" };
+ const int nf = 6;
char fond[64] = "0,00", rate[16] = "20,6";
- struct tui_form_field ff[2];
+ int can_edit = strcmp(a->role, "owner") == 0 ||
+ strcmp(a->role, "bookkeeper") == 0;
for (;;) {
if (g_quit)
return;
+ char args[48];
+ snprintf(args, sizeof args, "{\"id\":%lld}", (long long)a->fy);
+ char *resp = client_rpc(&a->conn, "fiscal_year.get", a->session,
+ a->org, args);
+ if (!resp || !client_ok(resp)) {
+ show_error("Bokslut", resp);
+ free(resp);
+ return;
+ }
+ char *vals[6];
+ struct tui_form_field ff[8];
+ int64_t div_ore = jint_val(resp, "result.dividend_ore", 0);
memset(ff, 0, sizeof ff);
- ff[0].label = "Periodiseringsfond (kr)";
- ff[0].value = fond;
- ff[0].cap = sizeof fond;
+ for (int i = 0; i < nf; i++) {
+ vals[i] = xcalloc(512, 1);
+ if (i != 2) {
+ char path[64], *v;
+ snprintf(path, sizeof path, "result.%s", keys[i]);
+ v = jstr_dup(resp, path);
+ snprintf(vals[i], 512, "%s", v ? v : "");
+ free(v);
+ }
+ ff[i].label = labels[i];
+ ff[i].value = vals[i];
+ ff[i].cap = 512;
+ }
+ free(resp);
ff[0].kind = TUI_F_TEXT;
- ff[1].label = "Skattesats (%)";
- ff[1].value = rate;
- ff[1].cap = sizeof rate;
- ff[1].kind = TUI_F_TEXT;
- int r = tui_form_run("Bokslut", ff, 2, 1);
+ ff[1].kind = TUI_F_DATE;
+ ff[2].kind = TUI_F_AMOUNT;
+ ff[2].ore = &div_ore;
+ ff[3].kind = TUI_F_DATE;
+ ff[4].kind = TUI_F_TEXT;
+ ff[5].kind = TUI_F_TEXT;
+ ff[6].label = "Periodiseringsfond (kr)";
+ ff[6].value = fond;
+ ff[6].cap = sizeof fond;
+ ff[6].kind = TUI_F_TEXT;
+ ff[7].label = "Skattesats (%)";
+ ff[7].value = rate;
+ ff[7].cap = sizeof rate;
+ ff[7].kind = TUI_F_TEXT;
+ int r = tui_form_run("Bokslut", ff, nf + 2, can_edit);
+ if (r >= 0 && r < nf) {
+ yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
+ yyjson_mut_val *o = yyjson_mut_obj(d);
+ yyjson_mut_doc_set_root(d, o);
+ yyjson_mut_obj_add_int(d, o, "id", a->fy);
+ if (r == 2)
+ yyjson_mut_obj_add_int(d, o, "dividend_ore", div_ore);
+ else
+ yyjson_mut_obj_add_strcpy(d, o, keys[r], vals[r]);
+ char *uargs = yyjson_mut_write(d, 0, NULL);
+ yyjson_mut_doc_free(d);
+ char *rr = client_rpc(&a->conn, "fiscal_year.update", a->session,
+ a->org, uargs);
+ free(uargs);
+ if (!rr || !client_ok(rr))
+ show_error("Kunde inte spara", rr);
+ free(rr);
+ }
+ for (int i = 0; i < nf; i++)
+ free(vals[i]);
if (r == TUI_FORM_REFRESH)
bokslut_plan(a, fond, rate, 0);
else if (r == TUI_FORM_SUBMIT)
@@ -4711,6 +4710,17 @@ static void ar_amt_col(char *buf, size_t n, int width, int64_t ore)
snprintf(buf, n, "%*s", width, tmp);
}
+/* Sum and balance rows are derived figures and read as secondary. */
+static int ar_dim_label(const char *label)
+{
+ return strncmp(label, "Summa", 5) == 0 || strcmp(label, "Totalt") == 0 ||
+ strncmp(label, "Belopp vid", 10) == 0 ||
+ strcmp(label, "Årets resultat") == 0 ||
+ strcmp(label, "Resultat före skatt") == 0 ||
+ strcmp(label, "Rörelseresultat") == 0 ||
+ strcmp(label, "Resultat efter finansiella poster") == 0;
+}
+
static void ar_row(struct buf *t, const char *label, int64_t cur, int64_t prev,
int indent)
{
@@ -4719,7 +4729,8 @@ static void ar_row(struct buf *t, const char *label, int64_t cur, int64_t prev,
tui_pad_label(padded, sizeof padded, nm, 48);
ar_amt_col(a1, sizeof a1, 16, cur);
ar_amt_col(a2, sizeof a2, 16, prev);
- buf_line(t, "%s %s %s\n", padded, a1, a2);
+ buf_line(t, "%s%s %s %s\n", ar_dim_label(label) ? MK_DIM : "", padded,
+ a1, a2);
}
/* Equity table: four amount columns. */
@@ -4733,7 +4744,8 @@ static void ar_eq_row(struct buf *t, const char *label, int64_t ak,
ar_amt_col(c2, sizeof c2, 14, br);
ar_amt_col(c3, sizeof c3, 14, ar);
ar_amt_col(c4, sizeof c4, 14, ak + br + ar);
- buf_line(t, "%s %s %s %s %s\n", padded, c1, c2, c3, c4);
+ buf_line(t, "%s%s %s %s %s %s\n", ar_dim_label(label) ? MK_DIM : "",
+ padded, c1, c2, c3, c4);
}
static void ar_wrap(struct buf *t, const char *text)
@@ -4801,14 +4813,15 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
ar_sv_date(vstr(fyj, "agm_date"), agm_sv, sizeof agm_sv);
ar_sv_date(vstr(fyj, "dividend_date"), pay_sv, sizeof pay_sv);
- buf_line(t, "ÅRSREDOVISNING\n\n%s\nOrg nr %s\n\n", a->org_name, org_nr);
+ buf_line(t, MK_HEAD "ÅRSREDOVISNING\n\n%s\nOrg nr %s\n\n", a->org_name,
+ org_nr);
buf_line(t, "Årsredovisning för räkenskapsåret %s - %s\n\n", a->fy_start,
a->fy_end);
buf_line(t, "Styrelsen avger följande årsredovisning.\n\n");
buf_line(t, "Om inte annat särskilt anges, redovisas alla belopp i hela"
" kronor.\n");
- buf_line(t, "\nFastställelseintyg\n\n");
+ buf_line(t, "\n" MK_HEAD "Fastställelseintyg\n\n");
buf_line(t, "Undertecknad styrelseledamot i %s intygar, dels att denna"
" kopia av\nårsredovisningen stämmer överens med originalet,"
" dels att resultaträkningen\noch balansräkningen har"
@@ -4819,15 +4832,15 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
" ___________________________\n\nNamnförtydligande: %s\n",
city, agm_sv, first_board);
- buf_line(t, "\nFörvaltningsberättelse\n\nVerksamheten\n\nAllmänt om"
- " verksamheten\n");
+ buf_line(t, "\n" MK_HEAD "Förvaltningsberättelse\n\nVerksamheten\n\n"
+ "Allmänt om verksamheten\n");
if (description && *description)
ar_wrap(t, description);
else
buf_line(t, "[Kort beskrivning av verksamheten saknas — fyll i under"
" Företagsuppgifter.]\n");
buf_line(t, "\nBolaget har sitt säte i %s.\n", city);
- buf_line(t, "\nVäsentliga händelser under räkenskapsåret\n");
+ buf_line(t, "\n" MK_HEAD "Väsentliga händelser under räkenskapsåret\n");
if (events && *events)
ar_wrap(t, events);
else
@@ -4839,7 +4852,7 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
for (int i = 0; i < nyears; i++)
off += snprintf(row + off, sizeof row - (size_t)off, "%9s",
years[i].label);
- buf_line(t, "\n%s\n\n", row);
+ buf_line(t, "\n" MK_HEAD "%s\n\n", row);
off = snprintf(row, sizeof row, "%-38s", "Nettoomsättning, tkr");
for (int i = 0; i < nyears; i++)
off += snprintf(row + off, sizeof row - (size_t)off, "%9lld",
@@ -4874,7 +4887,7 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
"Årets");
off += snprintf(row + off, sizeof row - (size_t)off, "%14s",
"Totalt");
- buf_line(t, "\n%s\n", row);
+ buf_line(t, "\n" MK_HEAD "%s\n", row);
buf_line(t, "%s\n", " resultat"
" resultat");
int has_disp = movement != 0;
@@ -4890,8 +4903,8 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
}
int64_t dispon = br_ub + ars;
- buf_line(t, "\nResultatdisposition\n\nTill årsstämmans förfogande står"
- " följande vinstmedel:\n");
+ buf_line(t, "\n" MK_HEAD "Resultatdisposition\n\nTill årsstämmans"
+ " förfogande står följande vinstmedel:\n");
ar_row(t, "Balanserat resultat", br_ub, 0, 1);
ar_row(t, "Årets resultat", ars, 0, 1);
ar_row(t, "Totalt", dispon, 0, 1);
@@ -4902,7 +4915,7 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
ar_row(t, "Totalt", dispon, 0, 1);
if (dividend > 0) {
- buf_line(t, "\nStyrelsens yttrande över den föreslagna"
+ buf_line(t, "\n" MK_HEAD "Styrelsens yttrande över den föreslagna"
" vinstutdelningen\n\n");
if (shares > 0) {
char per[48];
@@ -4922,8 +4935,8 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
" 3 § 2 st. (försiktighetsregeln).\n");
}
- buf_line(t, "\nResultaträkning\nSamtliga belopp anges i svenska"
- " kronor.\n\n");
+ buf_line(t, "\n" MK_HEAD "Resultaträkning\nSamtliga belopp anges i"
+ " svenska kronor.\n\n");
{
char h1[40], h2[40], row[512];
snprintf(h1, sizeof h1, "%s - %s", cur->from, cur->to);
@@ -4937,14 +4950,14 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
off += snprintf(row + off, sizeof row - (size_t)off, "%17s", h2);
buf_line(t, "%s\n", row);
}
- buf_line(t, "Rörelseintäkter, lagerförändringar m.m.\n");
+ buf_line(t, MK_HEAD "Rörelseintäkter, lagerförändringar m.m.\n");
ar_row(t, "Nettoomsättning", cur->groups[0],
prev ? prev->groups[0] : 0, 1);
int64_t rev_cur = cur->groups[0] + cur->groups[1];
int64_t rev_pre = prev ? prev->groups[0] + prev->groups[1] : 0;
ar_row(t, "Summa Rörelseintäkter, lagerförändringar m.m.", rev_cur,
rev_pre, 1);
- buf_line(t, "\nRörelsekostnader\n");
+ buf_line(t, "\n" MK_HEAD "Rörelsekostnader\n");
int64_t cost_cur = 0, cost_pre = 0;
for (size_t g = 2; g < AR_GROUPS; g++) {
int64_t c = cur->groups[g], p = prev ? prev->groups[g] : 0;
@@ -4965,7 +4978,7 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
int64_t btax = cur->res_fin + cur->disp;
int64_t btax_pre = prev ? prev->res_fin + prev->disp : 0;
if (cur->disp || (prev && prev->disp)) {
- buf_line(t, "\nBokslutsdispositioner\n");
+ buf_line(t, "\n" MK_HEAD "Bokslutsdispositioner\n");
ar_row(t, "Summa", cur->disp, prev ? prev->disp : 0, 1);
}
ar_row(t, "Resultat före skatt", btax, btax_pre, 0);
@@ -4975,7 +4988,7 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
buf_line(t, "\n");
ar_row(t, "Årets resultat", ars, prev_ars, 0);
- buf_line(t, "\nBalansräkning\n\n");
+ buf_line(t, "\n" MK_HEAD "Balansräkning\n\n");
{
char h1[40], h2[40], row[256];
snprintf(h1, sizeof h1, "%s", cur->to);
@@ -4989,10 +5002,10 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
off += snprintf(row + off, sizeof row - (size_t)off, "%17s", h2);
buf_line(t, "%s\n", row);
}
- buf_line(t, "Tillgångar\nAnläggningstillgångar\n");
+ buf_line(t, MK_HEAD "Tillgångar\nAnläggningstillgångar\n");
ar_row(t, "Summa anläggningstillgångar", ar_bs_sum(bs, 1000, 1399),
ar_bs_ib_sum(bs, 1000, 1399), 1);
- buf_line(t, "Omsättningstillgångar\n");
+ buf_line(t, MK_HEAD "Omsättningstillgångar\n");
int64_t v = ar_bs_sum(bs, 1400, 1499), vp = ar_bs_ib_sum(bs, 1400, 1499);
if (v || vp)
ar_row(t, "Varulager", v, vp, 0);
@@ -5006,11 +5019,12 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
ar_bs_ib_sum(bs, 1900, 1999), 0);
ar_row(t, "Summa tillgångar", ar_bs_sum(bs, 1000, 1999),
ar_bs_ib_sum(bs, 1000, 1999), 0);
- buf_line(t, "\nEget kapital och skulder\nBundet eget kapital\n");
+ buf_line(t, "\n" MK_HEAD "Eget kapital och skulder\nBundet eget"
+ " kapital\n");
ar_row(t, "Aktiekapital", ak_ub, ak_ib, 1);
ar_row(t, "Summa bundet eget kapital", ar_bs_sum(bs, 2000, 2090),
ar_bs_ib_sum(bs, 2000, 2090), 1);
- buf_line(t, "Fritt eget kapital\n");
+ buf_line(t, MK_HEAD "Fritt eget kapital\n");
ar_row(t, "Balanserat resultat", br_ub, br_ib, 1);
ar_row(t, "Årets resultat", ars, prev_ars, 1);
ar_row(t, "Summa fritt eget kapital", ar_bs_sum(bs, 2091, 2098) + ars,
@@ -5025,7 +5039,7 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
ar_bs_ib_sum(bs, 2100, 2199), 0);
ar_row(t, "Avsättningar", ar_bs_sum(bs, 2200, 2299),
ar_bs_ib_sum(bs, 2200, 2299), 0);
- buf_line(t, "Kortfristiga skulder\n");
+ buf_line(t, MK_HEAD "Kortfristiga skulder\n");
ar_row(t, "Skatteskulder", ar_bs_sum(bs, 2500, 2599),
ar_bs_ib_sum(bs, 2500, 2599), 1);
ar_row(t, "Mervärdesskatt", ar_bs_sum(bs, 2600, 2699),
@@ -5042,20 +5056,21 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
ar_bs_sum(bs, 2000, 2999) + ars,
ar_bs_ib_sum(bs, 2000, 2999) + prev_ars, 0);
- buf_line(t, "\nNoter\n\nNot 1 Redovisnings- och värderingsprinciper\n");
+ buf_line(t, "\n" MK_HEAD "Noter\n\nNot 1 Redovisnings- och"
+ " värderingsprinciper\n");
buf_line(t, "Årsredovisningen är upprättad i enlighet med"
" årsredovisningslagen och\nBFNAR 2016:10 Årsredovisning i"
" mindre företag.\n");
- buf_line(t, "\nNot 2 Medelantal anställda\n");
+ buf_line(t, "\n" MK_HEAD "Not 2 Medelantal anställda\n");
buf_line(t, "Medelantalet anställda under räkenskapsåret har uppgått"
" till %s.\n", employees && *employees ? employees
: "[antal]");
if (notes && *notes) {
- buf_line(t, "\nNot 3 Övriga upplysningar\n");
+ buf_line(t, "\n" MK_HEAD "Not 3 Övriga upplysningar\n");
ar_wrap(t, notes);
}
- buf_line(t, "\nUnderskrifter\n\n%s %s\n\n", city, agm_sv);
+ buf_line(t, "\n" MK_HEAD "Underskrifter\n\n%s %s\n\n", city, agm_sv);
if (board_text && *board_text)
buf_line(t, "%s", board_text);
else
@@ -5063,6 +5078,24 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
" Företagsuppgifter.]\n");
}
+/* The pager text carries style markers that must not reach the saved file. */
+static void strip_markup(const char *in, struct buf *out)
+{
+ for (const char *p = in; *p;) {
+ const char *nl = strchr(p, '\n');
+ size_t len = nl ? (size_t)(nl - p) : strlen(p);
+ if (len && p[0] >= TUI_MARK_HEADING && p[0] <= TUI_MARK_RULE) {
+ p++;
+ len--;
+ }
+ buf_append(out, p, len);
+ if (!nl)
+ break;
+ buf_append(out, "\n", 1);
+ p = nl + 1;
+ }
+}
+
static void arsredovisning_save(struct app *a, const char *text)
{
const char *home = getenv("HOME");
@@ -5089,16 +5122,21 @@ static void arsredovisning_save(struct app *a, const char *text)
*p = '-';
snprintf(full + strlen(full), sizeof full - strlen(full),
"Årsredovisning %s.txt", *name ? name : "utkast");
+ struct buf clean;
+ buf_init(&clean);
+ strip_markup(text, &clean);
FILE *f = fopen(full, "wb");
- size_t n = strlen(text);
- if (!f || fwrite(text, 1, n, f) != n) {
+ size_t n = clean.len;
+ if (!f || fwrite(clean.p, 1, n, f) != n) {
if (f)
fclose(f);
+ buf_free(&clean);
tui_message("Årsredovisning", "Kunde inte skriva %s: %s", full,
strerror(errno));
return;
}
fclose(f);
+ buf_free(&clean);
tui_message("Årsredovisning", "Sparat: %s\n\nFilen är ett utkast i textform"
" att redigera vidare (t.ex. i Word).", full);
}
@@ -5258,10 +5296,9 @@ static void open_scene(struct app *a, const char *scene)
settings_screen(a);
else if (strcmp(scene, "company") == 0)
company_screen(a);
- else if (strcmp(scene, "bokslut") == 0)
+ else if (strcmp(scene, "bokslut") == 0 ||
+ strcmp(scene, "yearinfo") == 0) /* yearinfo: ^R compatibility */
bokslut_screen(a);
- else if (strcmp(scene, "yearinfo") == 0)
- yearinfo_screen(a);
}
static void do_reload(struct app *a, const char *self)
@@ -5296,10 +5333,14 @@ static void do_reload(struct app *a, const char *self)
}
static const char *const MAIN_ITEMS[] = {
- "Verifikat", "Underlag (inkorg)", "Ingående balans",
- "Mallar", "Rapporter", "Revision",
- "Inställningar", "Företagsuppgifter", "Bokslut",
- "Information om året", "Räkenskapsår", "Logga ut / avsluta",
+ MK_SECTION "Bokföring",
+ "Verifikat", "Underlag (inkorg)", "Ingående balans",
+ MK_SECTION "Rapporter & bokslut",
+ "Rapporter", "Bokslut",
+ MK_SECTION "Register",
+ "Mallar", "Företagsuppgifter", "Inställningar",
+ MK_SECTION "Övrigt",
+ "Revision", "Räkenskapsår", "Logga ut / avsluta",
};
static void dashboard(struct app *a)
@@ -5308,7 +5349,9 @@ static void dashboard(struct app *a)
for (;;) {
if (g_reload)
return;
- int sel = tui_menu("Bokf", MAIN_ITEMS, 12, 1, &last_sel);
+ int sel = tui_menu("Bokf", MAIN_ITEMS,
+ (int)(sizeof MAIN_ITEMS / sizeof MAIN_ITEMS[0]), 1,
+ &last_sel);
snprintf(g_scene, sizeof g_scene, "%s", "dashboard");
if (g_quit || g_reload)
return;
@@ -5321,41 +5364,40 @@ static void dashboard(struct app *a)
if (sel < 0)
continue; /* Esc/back at the top level never exits */
switch (sel) {
- case 0:
- open_scene(a, "vouchers");
- break;
case 1:
- open_scene(a, "inbox");
+ open_scene(a, "vouchers");
break;
case 2:
- open_scene(a, "ib");
+ open_scene(a, "inbox");
break;
case 3:
- open_scene(a, "templates");
- break;
- case 4:
- open_scene(a, "reports");
+ open_scene(a, "ib");
break;
case 5:
- open_scene(a, "audit");
+ open_scene(a, "reports");
break;
case 6:
- open_scene(a, "settings");
- break;
- case 7:
- open_scene(a, "company");
+ open_scene(a, "bokslut");
break;
case 8:
- open_scene(a, "bokslut");
+ open_scene(a, "templates");
break;
case 9:
- open_scene(a, "yearinfo");
+ open_scene(a, "company");
break;
case 10:
+ open_scene(a, "settings");
+ break;
+ case 12:
+ open_scene(a, "audit");
+ break;
+ case 13:
select_fiscal_year(a);
break;
- case 11:
+ case 14:
return;
+ default:
+ break; /* section header */
}
}
}
@@ -5634,8 +5676,8 @@ static void usage(FILE *f)
" --fy ID select fiscal year directly\n"
" --session ID reuse an open session (or BOKFD_SESSION)\n"
" --screen NAME start in a view: dashboard, vouchers, inbox,\n"
- " ib, templates, reports, bokslut, yearinfo,\n"
- " audit, settings, company\n"
+ " ib, templates, reports, bokslut (yearinfo\n"
+ " is an alias), audit, settings, company\n"
" --version\n");
}