summaryrefslogtreecommitdiff
path: root/clients
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-21 21:43:44 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-21 21:43:44 +0200
commit62a92f0af0442c78277c3c97c9efe6fef8145625 (patch)
tree3802a62904880da22bf84a44e9ece3cdff9cd8aa /clients
parentf1eeb15781a044a9451ee08b91c63feb395b4767 (diff)
downloadbokf-0.1.60.tar.gz
bokf-0.1.60.zip
settings: per-feature voucher series and console invoice number; tui: F9 hintsv0.1.60
Diffstat (limited to 'clients')
-rw-r--r--clients/screens_bokslut.c2
-rw-r--r--clients/screens_ib.c4
-rw-r--r--clients/screens_invoices.c2
-rw-r--r--clients/screens_payroll.c4
-rw-r--r--clients/screens_settings.c89
-rw-r--r--clients/screens_templates.c2
-rw-r--r--clients/screens_vouchers.c19
-rw-r--r--clients/tui.c4
-rw-r--r--clients/ui.c7
-rw-r--r--clients/ui.h1
10 files changed, 105 insertions, 29 deletions
diff --git a/clients/screens_bokslut.c b/clients/screens_bokslut.c
index 3f69982..96c6fe9 100644
--- a/clients/screens_bokslut.c
+++ b/clients/screens_bokslut.c
@@ -242,7 +242,7 @@ void bokslut_screen(struct app *a)
ff[7].cap = sizeof rate;
ff[7].kind = TUI_F_TEXT;
tui_form_hint("upp/ned/Tab = flytta Enter = ändra/utför F5 = visa"
- " bokslutsplan ^Enter/F9 = bokför planen (frågar"
+ " bokslutsplan F9 = bokför planen (frågar"
" först) Esc/q = tillbaka ^C = avsluta");
int r = tui_form_run_actions("Bokslut", ff, nf + 2, can_edit, acts, 5,
NULL, &sel);
diff --git a/clients/screens_ib.c b/clients/screens_ib.c
index 8950667..2d62297 100644
--- a/clients/screens_ib.c
+++ b/clients/screens_ib.c
@@ -167,7 +167,7 @@ static int ib_post_delta(struct ibform *ib)
yyjson_mut_doc_set_root(d, o);
yyjson_mut_obj_add_strcpy(d, o, "date", a->fy_start);
yyjson_mut_obj_add_strcpy(d, o, "description", "Ingående balans");
- yyjson_mut_obj_add_strcpy(d, o, "series", "IB");
+ yyjson_mut_obj_add_strcpy(d, o, "series", a->ib_series);
char *ref = util_random_id("tui-", 8);
yyjson_mut_obj_add_strcpy(d, o, "client_ref", ref);
free(ref);
@@ -272,7 +272,7 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
for (;;) {
int rr = tui_rt_run("Ingående balans", &ib.rt,
"Tab = byta fält F5 = validera ^X = rensa rad"
- " ^Enter/F9 = spara Esc = avbryt");
+ " F9 = spara Esc = avbryt");
if (rr == -1)
return 0;
char msg[256];
diff --git a/clients/screens_invoices.c b/clients/screens_invoices.c
index ca59eef..8fb32eb 100644
--- a/clients/screens_invoices.c
+++ b/clients/screens_invoices.c
@@ -1092,7 +1092,7 @@ static int64_t invoices_new(struct app *a)
tui_rt_set_fields(&f.rt, ff, 7);
const char *hint =
"Enter = välj/ändra Tab = byta fält F5 = förhandsvisa"
- " ^Enter/F9 = utfärda Esc = avbryt";
+ " F9 = utfärda Esc = avbryt";
int64_t out = 0;
for (;;) {
int rr = tui_rt_run("Ny faktura", &f.rt, hint);
diff --git a/clients/screens_payroll.c b/clients/screens_payroll.c
index 23d765f..234778c 100644
--- a/clients/screens_payroll.c
+++ b/clients/screens_payroll.c
@@ -1101,7 +1101,7 @@ static int64_t payroll_run_screen(struct app *a, int64_t id)
{ status, -1, NULL },
{ totals, -1, NULL },
{ "Förhandsvisa (F5)", 1, NULL },
- { "Bokför körning (^Enter/F9)", post_reason ? 0 : 1,
+ { "Bokför körning (F9)", post_reason ? 0 : 1,
post_reason },
{ "Lönebesked (PDF)", payslip_reason ? 0 : 1, payslip_reason },
{ "AGI-underlag", agi_reason ? 0 : 1, agi_reason },
@@ -1124,7 +1124,7 @@ static int64_t payroll_run_screen(struct app *a, int64_t id)
snprintf(title, sizeof title, "Ny lönekörning");
int editable = can_write && !posted;
tui_form_hint("upp/ned/Tab = flytta Enter = ändra/utför"
- " F5 = förhandsvisa ^Enter/F9 = bokför"
+ " F5 = förhandsvisa F9 = bokför"
" Esc/q = tillbaka ^C = avsluta");
int ret = tui_form_run_actions(title, ff, 2, editable, acts, 7, NULL,
&focus);
diff --git a/clients/screens_settings.c b/clients/screens_settings.c
index 8a31620..7730ff4 100644
--- a/clients/screens_settings.c
+++ b/clients/screens_settings.c
@@ -199,6 +199,7 @@ struct setting_field {
const char *const *choices;
int nchoices;
int mask;
+ int invoice_number;
};
static void setting_save(struct app *a, const struct setting_field *f,
@@ -218,9 +219,11 @@ static void setting_save(struct app *a, const struct setting_field *f,
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)
+ if (strcmp(f->key, "series_voucher") == 0)
snprintf(a->default_series, sizeof a->default_series, "%s",
value);
+ else if (strcmp(f->key, "series_ib") == 0)
+ snprintf(a->ib_series, sizeof a->ib_series, "%s", value);
else if (strcmp(f->key, "attachment_dir") == 0)
snprintf(a->attachment_dir, sizeof a->attachment_dir, "%s",
value);
@@ -283,12 +286,42 @@ static void settings_form(struct app *a, const char *title,
acts[0].enabled = -1;
}
free(resp);
+ for (int i = 0; i < n; i++) {
+ if (!fields[i].invoice_number)
+ continue;
+ char *seq = client_rpc(&a->conn, "invoice.sequence_get",
+ a->session, a->org, "{}");
+ if (seq && client_ok(seq))
+ snprintf(vals[i], 512, "%lld",
+ (long long)jint_val(seq, "result.next_number", 1));
+ free(seq);
+ }
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) {
+ if (r >= 0 && r < n && fields[r].invoice_number) {
+ if (!(a->role[0] && strcmp(a->role, "owner") == 0)) {
+ tui_message(title, "Kräver ägarbehörighet.");
+ } else {
+ char *end = NULL;
+ long v = strtol(vals[r], &end, 10);
+ if (!end || *end || v < 1) {
+ tui_message(title,
+ "Nästa fakturanummer måste vara ett positivt"
+ " heltal.");
+ } else {
+ char args[64];
+ snprintf(args, sizeof args, "{\"next_number\":%ld}", v);
+ char *rr = client_rpc(&a->conn, "invoice.sequence_set",
+ a->session, a->org, args);
+ if (!rr || !client_ok(rr))
+ show_error(title, rr);
+ free(rr);
+ }
+ }
+ } else if (r >= 0 && r < n) {
const char *val = vals[r];
if (fields[r].choices)
val = fields[r].choices[choice[r]];
@@ -304,21 +337,29 @@ static void settings_form(struct app *a, const char *title,
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 },
+ { "invoice_next_number", "Nästa fakturanummer", NULL, 0, 0, 1 },
+ { "invoice_receivable_account", "Fordringskonto", NULL, 0, 0, 0 },
+ { "invoice_revenue_account", "Intäktskonto", NULL, 0, 0, 0 },
+ { "invoice_bankgiro", "Bankgiro", NULL, 0, 0, 0 },
+ { "invoice_our_ref", "Vår referens (faktura)", NULL, 0, 0, 0 },
+};
+
+static const struct setting_field SERIES_SETTINGS[] = {
+ { "series_voucher", "Manuella verifikat", NULL, 0, 0, 0 },
+ { "series_invoice", "Fakturor", NULL, 0, 0, 0 },
+ { "series_payroll", "Lön", NULL, 0, 0, 0 },
+ { "series_bokslut", "Bokslut", NULL, 0, 0, 0 },
+ { "series_ib", "Ingående balans", NULL, 0, 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 },
+ { "smtp_host", "SMTP-server", NULL, 0, 0, 0 },
+ { "smtp_port", "SMTP-port", NULL, 0, 0, 0 },
+ { "smtp_user", "SMTP-användare", NULL, 0, 0, 0 },
+ { "smtp_from", "SMTP-avsändare", NULL, 0, 0, 0 },
+ { "smtp_reply_to", "SMTP-svarsadress", NULL, 0, 0, 0 },
+ { "smtp_security", "SMTP-säkerhet", SECURITY_CHOICES, 3, 0, 0 },
+ { "smtp_password", "SMTP-lösenord", NULL, 0, 1, 0 },
};
static int settings_can_write(struct app *a)
@@ -343,6 +384,14 @@ static void smtp_settings_screen(struct app *a)
settings_can_write(a), 1, &sel);
}
+static void series_settings_screen(struct app *a)
+{
+ static int sel = 0;
+ settings_form(a, "Verifikationsserier", SERIES_SETTINGS,
+ (int)(sizeof SERIES_SETTINGS / sizeof SERIES_SETTINGS[0]),
+ settings_can_write(a), 0, &sel);
+}
+
static const char *const SYSTEM_ITEMS[] = {
"Skattetabeller",
"Revision",
@@ -435,6 +484,7 @@ static void company_data_screen(struct app *a)
static const char *const COMPANY_ITEMS[] = {
"Företagsuppgifter",
"Fakturauppgifter",
+ "Verifikationsserier",
"E-post (SMTP)",
"Styrelseledamöter",
MK_SECTION "Register",
@@ -462,18 +512,21 @@ void company_screen(struct app *a)
invoice_settings_screen(a);
break;
case 2:
- smtp_settings_screen(a);
+ series_settings_screen(a);
break;
case 3:
+ smtp_settings_screen(a);
+ break;
+ case 4:
board_screen(a);
break;
- case 5:
+ case 6:
employees_screen(a);
break;
- case 6:
+ case 7:
customers_screen(a);
break;
- case 7:
+ case 8:
rules_screen(a);
break;
default:
diff --git a/clients/screens_templates.c b/clients/screens_templates.c
index 5777742..e55028d 100644
--- a/clients/screens_templates.c
+++ b/clients/screens_templates.c
@@ -234,7 +234,7 @@ static int template_form(struct app *a, const char *load_name)
tui_rt_set_fields(&tf.rt, ff, 3);
const char *title = load_name ? "Redigera mall" : "Ny mall";
const char *hint = "Enter = ändra fält Tab = byta fält F5 = validera"
- " ^X = rensa rad ^Enter/F9 = spara Esc = avbryt";
+ " ^X = rensa rad F9 = spara Esc = avbryt";
for (;;) {
int rr = tui_rt_run(title, &tf.rt, hint);
if (rr == -1)
diff --git a/clients/screens_vouchers.c b/clients/screens_vouchers.c
index 5e1b006..d884b6e 100644
--- a/clients/screens_vouchers.c
+++ b/clients/screens_vouchers.c
@@ -743,6 +743,23 @@ static int64_t vform_post(struct vform *vf, int dry)
replayed ? " — redan bokfört (idempotent)" : "");
free(ser);
free(r);
+ if (strcmp(vf->series, a->default_series) != 0) {
+ yyjson_mut_doc *sd = yyjson_mut_doc_new(NULL);
+ yyjson_mut_val *so = yyjson_mut_obj(sd);
+ yyjson_mut_doc_set_root(sd, so);
+ yyjson_mut_obj_add_strcpy(sd, so, "key", "series_voucher");
+ yyjson_mut_obj_add_strcpy(sd, so, "value", vf->series);
+ char *sargs = yyjson_mut_write(sd, 0, NULL);
+ yyjson_mut_doc_free(sd);
+ if (sargs) {
+ char *sr = client_rpc(&a->conn, "settings.set",
+ a->session, a->org, sargs);
+ free(sr);
+ free(sargs);
+ }
+ snprintf(a->default_series, sizeof a->default_series, "%s",
+ vf->series);
+ }
free(args);
return id;
}
@@ -836,7 +853,7 @@ int64_t vouchers_new_prefill(struct app *a, const struct voucher_prefill *p)
tui_rt_normalize(&vf.rt);
const char *hint = "Enter = ändra fält Tab = byta fält F4 = mall"
" ^F = bifoga fil F5 = validera ^X = rensa rad"
- " ^Enter/F9 = bokför Esc = avbryt";
+ " F9 = bokför Esc = avbryt";
for (;;) {
int rr = tui_rt_run("Nytt verifikat", &vf.rt, hint);
if (rr == -1)
diff --git a/clients/tui.c b/clients/tui.c
index 30761a8..db5599d 100644
--- a/clients/tui.c
+++ b/clients/tui.c
@@ -1615,7 +1615,7 @@ static int form_run(const char *title, struct tui_form_field *f, int nf,
h = na > 0
? (can_edit
? "upp/ned/Tab = flytta Enter = ändra/utför"
- " F5 = uppdatera ^Enter/F9 = spara"
+ " F5 = uppdatera F9 = spara"
" Esc/q = tillbaka ^C = avsluta"
: "upp/ned/Tab = flytta Enter = utför"
" F5 = uppdatera Esc/q = tillbaka"
@@ -1623,7 +1623,7 @@ static int form_run(const char *title, struct tui_form_field *f, int nf,
" (endast behöriga kan ändra)")
: (can_edit
? "upp/ned/Home/End Enter = ändra"
- " F5 = uppdatera ^Enter/F9 = spara"
+ " F5 = uppdatera F9 = spara"
" Esc/q = tillbaka ^C = avsluta"
: "upp/ned/Home/End F5 = uppdatera"
" Esc/q = tillbaka ^C = avsluta"
diff --git a/clients/ui.c b/clients/ui.c
index ca6906f..e6f6e07 100644
--- a/clients/ui.c
+++ b/clients/ui.c
@@ -217,12 +217,17 @@ void app_refresh_context(struct app *a)
free(resp);
snprintf(a->default_series, sizeof a->default_series, "%s", "A");
+ snprintf(a->ib_series, sizeof a->ib_series, "%s", "IB");
resp = client_rpc(&a->conn, "settings.get", a->session, a->org, "{}");
if (resp && client_ok(resp)) {
- char *ser = jstr_dup(resp, "result.default_series");
+ char *ser = jstr_dup(resp, "result.series_voucher");
if (ser && *ser)
snprintf(a->default_series, sizeof a->default_series, "%s", ser);
free(ser);
+ char *ib = jstr_dup(resp, "result.series_ib");
+ if (ib && *ib)
+ snprintf(a->ib_series, sizeof a->ib_series, "%s", ib);
+ free(ib);
}
free(resp);
diff --git a/clients/ui.h b/clients/ui.h
index b37eb4f..b4ce5f5 100644
--- a/clients/ui.h
+++ b/clients/ui.h
@@ -42,6 +42,7 @@ struct app {
int64_t employee_sel; /* last selected employee id in the list view */
int64_t payroll_sel; /* last selected payroll run id in the list view */
char default_series[16];
+ char ib_series[16];
char attachment_dir[256];
long max_attachment_bytes;
char fy_label[64];