aboutsummaryrefslogtreecommitdiff
path: root/clients/screens_settings.c
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-21 11:45:54 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-21 11:45:54 +0200
commite1dd7112e0346914710566660625da0f6b866e8a (patch)
treec4b0d5c7f6f62c1c4c38245e7c001b3fd4b03e74 /clients/screens_settings.c
parent182f0a592b6fe97fc1ff87add227f9d52a4ef902 (diff)
downloadbokf-main.tar.gz
bokf-main.zip
tui: payroll screens, Företag/System menu and settings splitHEADv0.1.55main
Diffstat (limited to 'clients/screens_settings.c')
-rw-r--r--clients/screens_settings.c348
1 files changed, 182 insertions, 166 deletions
diff --git a/clients/screens_settings.c b/clients/screens_settings.c
index 6440150..c6a46c5 100644
--- a/clients/screens_settings.c
+++ b/clients/screens_settings.c
@@ -193,22 +193,181 @@ static void board_screen(struct app *a)
}
}
+struct setting_field {
+ const char *key;
+ const char *label;
+ const char *const *choices;
+ int nchoices;
+ int mask;
+};
+
+static void setting_save(struct app *a, const struct setting_field *f,
+ const char *value, const char *title)
+{
+ if (f->mask && !*value)
+ return; /* empty secret means keep the stored one */
+ 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_strcpy(d, o, "key", f->key);
+ yyjson_mut_obj_add_strcpy(d, o, "value", value);
+ char *args = yyjson_mut_write(d, 0, NULL);
+ yyjson_mut_doc_free(d);
+ if (!args)
+ return;
+ char *rr = client_rpc(&a->conn, "settings.set", a->session, a->org, args);
+ free(args);
+ if (rr && client_ok(rr)) {
+ if (strcmp(f->key, "default_series") == 0)
+ snprintf(a->default_series, sizeof a->default_series, "%s",
+ value);
+ else if (strcmp(f->key, "attachment_dir") == 0)
+ snprintf(a->attachment_dir, sizeof a->attachment_dir, "%s",
+ value);
+ } else {
+ show_error(title, rr);
+ }
+ free(rr);
+}
+
+static void settings_form(struct app *a, const char *title,
+ const struct setting_field *fields, int n,
+ int can_edit, int show_password_status, int *focus)
+{
+ for (;;) {
+ if (g_quit)
+ return;
+ char *resp = client_rpc(&a->conn, "settings.get", a->session, a->org,
+ "{}");
+ if (!resp || !client_ok(resp)) {
+ show_error(title, resp);
+ free(resp);
+ return;
+ }
+ char *vals[8];
+ int choice[8];
+ struct tui_form_field ff[8];
+ memset(ff, 0, sizeof ff);
+ for (int i = 0; i < n; i++) {
+ char path[64];
+ vals[i] = xcalloc(512, 1);
+ snprintf(path, sizeof path, "result.%s", fields[i].key);
+ char *v = jstr_dup(resp, path);
+ snprintf(vals[i], 512, "%s", v ? v : "");
+ free(v);
+ ff[i].label = fields[i].label;
+ ff[i].value = vals[i];
+ ff[i].cap = 512;
+ ff[i].kind = TUI_F_TEXT;
+ ff[i].mask = fields[i].mask;
+ choice[i] = 0;
+ if (fields[i].choices) {
+ ff[i].kind = TUI_F_CHOICE;
+ ff[i].choices = fields[i].choices;
+ ff[i].nchoices = fields[i].nchoices;
+ ff[i].choice = &choice[i];
+ for (int k = 0; k < fields[i].nchoices; k++)
+ if (strcmp(vals[i], fields[i].choices[k]) == 0)
+ choice[i] = k;
+ }
+ }
+ char pwstatus[192];
+ struct tui_form_action acts[1];
+ memset(acts, 0, sizeof acts);
+ if (show_password_status) {
+ int pw_set = jbool_val(resp, "result.smtp_password_set", 0);
+ snprintf(pwstatus, sizeof pwstatus,
+ "SMTP-lösenord (lämna tomt för oförändrat): %s",
+ pw_set ? "satt" : "inte satt");
+ acts[0].label = pwstatus;
+ acts[0].enabled = -1;
+ }
+ free(resp);
+ tui_form_hint("upp/ned/Home/End Enter = ändra F5 = uppdatera"
+ " Esc/q = tillbaka ^C = avsluta");
+ int r = tui_form_run_actions(title, ff, n, can_edit, acts,
+ show_password_status ? 1 : 0, NULL,
+ focus);
+ if (r >= 0 && r < n) {
+ const char *val = vals[r];
+ if (fields[r].choices)
+ val = fields[r].choices[choice[r]];
+ setting_save(a, &fields[r], val, title);
+ }
+ for (int i = 0; i < n; i++)
+ free(vals[i]);
+ if (r == TUI_FORM_BACK)
+ return;
+ }
+}
+
+static const char *const SECURITY_CHOICES[] = { "starttls", "tls", "plain" };
+
+static const struct setting_field INVOICE_SETTINGS[] = {
+ { "default_series", "Standardserie", NULL, 0, 0 },
+ { "invoice_receivable_account", "Fordringskonto", NULL, 0, 0 },
+ { "invoice_revenue_account", "Intäktskonto", NULL, 0, 0 },
+ { "invoice_bankgiro", "Bankgiro", NULL, 0, 0 },
+ { "invoice_our_ref", "Vår referens (faktura)", NULL, 0, 0 },
+};
+
+static const struct setting_field SMTP_SETTINGS[] = {
+ { "smtp_host", "SMTP-server", NULL, 0, 0 },
+ { "smtp_port", "SMTP-port", NULL, 0, 0 },
+ { "smtp_user", "SMTP-användare", NULL, 0, 0 },
+ { "smtp_from", "SMTP-avsändare", NULL, 0, 0 },
+ { "smtp_reply_to", "SMTP-svarsadress", NULL, 0, 0 },
+ { "smtp_security", "SMTP-säkerhet", SECURITY_CHOICES, 3, 0 },
+ { "smtp_password", "SMTP-lösenord", NULL, 0, 1 },
+};
+
+static const struct setting_field SYSTEM_SETTINGS[] = {
+ { "attachment_dir", "Bilagornas mapp", NULL, 0, 0 },
+};
+
+static int settings_can_write(struct app *a)
+{
+ return a->role[0] && (strcmp(a->role, "owner") == 0 ||
+ strcmp(a->role, "bookkeeper") == 0);
+}
+
+static void invoice_settings_screen(struct app *a)
+{
+ static int sel = 0;
+ settings_form(a, "Fakturauppgifter", INVOICE_SETTINGS,
+ (int)(sizeof INVOICE_SETTINGS / sizeof INVOICE_SETTINGS[0]),
+ settings_can_write(a), 0, &sel);
+}
+
+static void smtp_settings_screen(struct app *a)
+{
+ static int sel = 0;
+ settings_form(a, "E-post (SMTP)", SMTP_SETTINGS,
+ (int)(sizeof SMTP_SETTINGS / sizeof SMTP_SETTINGS[0]),
+ settings_can_write(a), 1, &sel);
+}
+
+void system_settings_screen(struct app *a)
+{
+ static int sel = 0;
+ settings_form(a, "Systeminställningar", SYSTEM_SETTINGS,
+ (int)(sizeof SYSTEM_SETTINGS / sizeof SYSTEM_SETTINGS[0]),
+ settings_can_write(a), 0, &sel);
+}
+
void company_screen(struct app *a)
{
static int sel = 0;
const int nf = (int)(sizeof COMPANY_FIELDS / sizeof COMPANY_FIELDS[0]);
- const int ninv = 2;
- static const char *const invkeys[2] = { "invoice_bankgiro",
- "invoice_our_ref" };
- static const char *const invlabels[2] = { "Bankgiro",
- "Vår referens (faktura)" };
int owner = a->role[0] && strcmp(a->role, "owner") == 0;
+ int writer = settings_can_write(a);
+ const char *settings_reason = writer ? NULL : "du har inte behörighet";
for (;;) {
if (g_quit)
return;
char *resp = client_rpc(&a->conn, "org.get", a->session, a->org, "{}");
if (!resp || !client_ok(resp)) {
- show_error("Företagsuppgifter", resp);
+ show_error("Bolaget", resp);
free(resp);
return;
}
@@ -233,26 +392,15 @@ void company_screen(struct app *a)
ff[i].kind = TUI_F_TEXT;
}
free(resp);
- char *sresp =
- client_rpc(&a->conn, "settings.get", a->session, a->org, "{}");
- for (int i = 0; i < ninv; i++) {
- char path[64];
- int idx = nf + i;
- vals[idx] = xcalloc(512, 1);
- snprintf(path, sizeof path, "result.%s", invkeys[i]);
- char *v = sresp ? jstr_dup(sresp, path) : NULL;
- snprintf(vals[idx], 512, "%s", v ? v : "");
- free(v);
- ff[idx].label = invlabels[i];
- ff[idx].value = vals[idx];
- ff[idx].cap = 512;
- ff[idx].kind = TUI_F_TEXT;
- }
- free(sresp);
- ff[nf + ninv].label = "Styrelseledamöter";
- ff[nf + ninv].kind = TUI_F_ACTION;
- int r = tui_form_run("Företagsuppgifter", ff, nf + ninv + 1, owner,
- &sel);
+ struct tui_form_action acts[3] = {
+ { "Fakturauppgifter…", writer ? 1 : 0, settings_reason },
+ { "E-post (SMTP)…", writer ? 1 : 0, settings_reason },
+ { "Styrelseledamöter", 1, NULL },
+ };
+ tui_form_hint("upp/ned/Tab = flytta Enter = ändra/utför"
+ " F5 = uppdatera Esc/q = tillbaka ^C = avsluta");
+ int r = tui_form_run_actions("Bolaget", ff, nf, owner, acts, 3, NULL,
+ &sel);
if (r >= 0 && r < nf) {
if (!company_field_valid(&COMPANY_FIELDS[r], vals[r])) {
tui_message("Ogiltigt värde", "%s", COMPANY_FIELDS[r].label);
@@ -275,150 +423,18 @@ void company_screen(struct app *a)
show_error("Kunde inte spara", rr);
free(rr);
}
- } else if (r >= nf && r < nf + ninv) {
- 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_strcpy(d, o, "key", invkeys[r - nf]);
- yyjson_mut_obj_add_strcpy(d, o, "value", vals[r]);
- char *args = yyjson_mut_write(d, 0, NULL);
- yyjson_mut_doc_free(d);
- char *rr = client_rpc(&a->conn, "settings.set", a->session,
- a->org, args);
- free(args);
- if (!rr || !client_ok(rr))
- show_error("Kunde inte spara", rr);
- free(rr);
}
- if (r == nf + ninv)
- board_screen(a);
- for (int i = 0; i < nf + ninv; i++)
+ for (int i = 0; i < nf; i++)
free(vals[i]);
- if (r == TUI_FORM_BACK)
+ if (r == TUI_FORM_ACTION + 0)
+ invoice_settings_screen(a);
+ else if (r == TUI_FORM_ACTION + 1)
+ smtp_settings_screen(a);
+ else if (r == TUI_FORM_ACTION + 2)
+ board_screen(a);
+ else if (r == TUI_FORM_BACK)
return;
}
}
-static const char *const SETTING_KEYS[] = {
- "default_series",
- "attachment_dir",
- "invoice_receivable_account",
- "invoice_revenue_account",
- "smtp_host",
- "smtp_port",
- "smtp_user",
- "smtp_from",
- "smtp_reply_to",
- "smtp_security",
- "smtp_password",
-};
-static const char *const SETTING_LABELS[] = {
- "Standardserie",
- "Bilagornas mapp",
- "Fordringskonto",
- "Intäktskonto",
- "SMTP-server",
- "SMTP-port",
- "SMTP-användare",
- "SMTP-avsändare",
- "SMTP-svarsadress",
- "SMTP-säkerhet",
- "SMTP-lösenord",
-};
-static const char *const SECURITY_CHOICES[] = { "starttls", "tls", "plain" };
-void settings_screen(struct app *a)
-{
- static int sel = 0;
- const int nset = (int)(sizeof SETTING_KEYS / sizeof SETTING_KEYS[0]);
- for (;;) {
- if (g_quit)
- return;
- char *resp =
- client_rpc(&a->conn, "settings.get", a->session, a->org, "{}");
- if (!resp || !client_ok(resp)) {
- show_error("Inställningar", resp);
- free(resp);
- return;
- }
- int pw_set = jbool_val(resp, "result.smtp_password_set", 0);
- char pwstatus[192];
- snprintf(pwstatus, sizeof pwstatus,
- "SMTP-lösenord (lämna tomt för oförändrat): %s",
- pw_set ? "satt" : "inte satt");
- char *vals[16];
- struct tui_form_field ff[16];
- memset(ff, 0, sizeof ff);
- int sec_choice = 0;
- for (int i = 0; i < nset; i++) {
- char path[64], *v;
- vals[i] = xcalloc(512, 1);
- snprintf(path, sizeof path, "result.%s", SETTING_KEYS[i]);
- v = jstr_dup(resp, path);
- snprintf(vals[i], 512, "%s", v ? v : "");
- free(v);
- const char *key = SETTING_KEYS[i];
- ff[i].label = SETTING_LABELS[i];
- ff[i].value = vals[i];
- ff[i].cap = 512;
- ff[i].kind = TUI_F_TEXT;
- if (strcmp(key, "smtp_security") == 0) {
- ff[i].kind = TUI_F_CHOICE;
- ff[i].choices = SECURITY_CHOICES;
- ff[i].nchoices = 3;
- ff[i].choice = &sec_choice;
- for (int k = 0; k < 3; k++)
- if (strcmp(vals[i], SECURITY_CHOICES[k]) == 0)
- sec_choice = k;
- } else if (strcmp(key, "smtp_password") == 0) {
- ff[i].mask = 1;
- }
- }
- free(resp);
- struct tui_form_action acts[1];
- memset(acts, 0, sizeof acts);
- acts[0].label = pwstatus;
- acts[0].enabled = -1; /* dim status row, not selectable */
- int r = tui_form_run_actions(
- "Inställningar", ff, nset, 1, acts, 1,
- "upp/ned/Home/End Enter = ändra F5 = uppdatera"
- " Esc/q = tillbaka ^C = avsluta",
- &sel);
- if (r >= 0 && r < nset) {
- const char *key = SETTING_KEYS[r];
- const char *val = vals[r];
- if (strcmp(key, "smtp_security") == 0)
- val = SECURITY_CHOICES[sec_choice];
- if (strcmp(key, "smtp_password") == 0 && !*vals[r]) {
- /* empty means "keep the stored secret" */
- } else {
- 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_strcpy(d, o, "key", key);
- yyjson_mut_obj_add_strcpy(d, o, "value", val);
- char *args = yyjson_mut_write(d, 0, NULL);
- yyjson_mut_doc_free(d);
- char *rr = client_rpc(&a->conn, "settings.set", a->session,
- a->org, args);
- free(args);
- if (rr && client_ok(rr)) {
- snprintf(vals[r], 512, "%s", val);
- if (strcmp(key, "default_series") == 0)
- snprintf(a->default_series,
- sizeof a->default_series, "%s", val);
- else if (strcmp(key, "attachment_dir") == 0)
- snprintf(a->attachment_dir,
- sizeof a->attachment_dir, "%s", val);
- } else {
- show_error("Kunde inte spara inställningen", rr);
- }
- free(rr);
- }
- }
- for (int i = 0; i < nset; i++)
- free(vals[i]);
- if (r == TUI_FORM_BACK)
- return;
- }
-}