summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clients/bokftui.c19
-rw-r--r--clients/screens_dashboard.c21
-rw-r--r--clients/ui.c6
-rw-r--r--clients/ui.h1
-rw-r--r--docs/PROTOCOL.md1
-rw-r--r--docs/STATE.md5
-rw-r--r--docs/TUI-GUIDELINES.md8
-rwxr-xr-xscripts/tui-golden.py33
8 files changed, 85 insertions, 9 deletions
diff --git a/clients/bokftui.c b/clients/bokftui.c
index 867e63e..19a63cf 100644
--- a/clients/bokftui.c
+++ b/clients/bokftui.c
@@ -286,7 +286,9 @@ static int login_screen(struct app *a)
}
}
-static int select_org(struct app *a)
+/* Org picker; the cursor starts on the current org. 0 when one was
+ chosen (a->org set), -1 on Esc or error. */
+int select_org(struct app *a)
{
char *resp = client_rpc(&a->conn, "session.list_orgs", a->session, 0, "{}");
if (!resp || !client_ok(resp)) {
@@ -319,17 +321,20 @@ static int select_org(struct app *a)
free(name);
free(role);
}
- int sel = tui_select_list("Välj organisation att representera", items, (int)n,
- 0, 0, NULL, 0, NULL, 0);
+ int start = 0;
+ for (size_t i = 0; i < n; i++)
+ if (ids[i] == a->org)
+ start = (int)i;
+ int sel = tui_select_list("Välj organisation att representera", items,
+ (int)n, start, 0, NULL, 0, NULL, 0);
for (size_t i = 0; i < n; i++)
free(items[i]);
free(items);
free(resp);
- if (sel < 0)
- return -1;
- a->org = ids[sel];
+ if (sel >= 0)
+ a->org = ids[sel];
free(ids);
- return 0;
+ return sel >= 0 ? 0 : -1;
}
static void usage(FILE *f)
diff --git a/clients/screens_dashboard.c b/clients/screens_dashboard.c
index 83b4f6a..127d4df 100644
--- a/clients/screens_dashboard.c
+++ b/clients/screens_dashboard.c
@@ -274,9 +274,27 @@ static const char *const MAIN_ITEMS[] = {
"Rapporter", "Bokslut",
"Bolaget", "System",
"Ingående balans", "Räkenskapsår",
+ "Byt bolag",
"Logga ut / avsluta",
};
+/* Picks another org and reloads everything that belongs to the org: its
+ context (name, role, current fiscal year, series) and the remembered
+ list selections. */
+static void switch_org(struct app *a)
+{
+ int64_t old = a->org;
+ if (select_org(a) != 0 || a->org == old)
+ return;
+ a->voucher_sel = 0;
+ a->invoice_sel = 0;
+ a->customer_sel = 0;
+ a->employee_sel = 0;
+ a->payroll_sel = 0;
+ app_refresh_context(a);
+ update_status(a);
+}
+
void dashboard(struct app *a)
{
static int last_sel = 0;
@@ -335,6 +353,9 @@ void dashboard(struct app *a)
select_fiscal_year(a);
break;
case 12:
+ switch_org(a);
+ break;
+ case 13:
return;
default:
break;
diff --git a/clients/ui.c b/clients/ui.c
index cda0883..2eb2dfe 100644
--- a/clients/ui.c
+++ b/clients/ui.c
@@ -183,6 +183,12 @@ void replace_x_into(const char *src, double x, char *dst, size_t cap)
void app_refresh_context(struct app *a)
{
+ a->org_name[0] = '\0';
+ a->role[0] = '\0';
+ a->fy = 0;
+ a->fy_label[0] = '\0';
+ a->fy_start[0] = '\0';
+ a->fy_end[0] = '\0';
char args[64];
snprintf(args, sizeof args, "{\"org\":%lld}", (long long)a->org);
char *resp = client_rpc(&a->conn, "session.use_org", a->session, 0, args);
diff --git a/clients/ui.h b/clients/ui.h
index 8574121..884adbf 100644
--- a/clients/ui.h
+++ b/clients/ui.h
@@ -76,6 +76,7 @@ void kr_whole(int64_t ore, char *buf, size_t n);
int parse_x_double(const char *s, double *out);
void replace_x_into(const char *src, double x, char *dst, size_t cap);
void app_refresh_context(struct app *a);
+int select_org(struct app *a);
char *rpc_dry(struct app *a, const char *cmd, const char *args);
char *read_file_b64(const char *path);
char *file_browser(struct app *a, const char *start_dir);
diff --git a/docs/PROTOCOL.md b/docs/PROTOCOL.md
index 2bd889f..ae1098f 100644
--- a/docs/PROTOCOL.md
+++ b/docs/PROTOCOL.md
@@ -935,6 +935,7 @@ Args: `name:type(values)[!][=default]`, `!` = required.
commands. Implemented screens (0.1.0-dev):
- **Inloggning** — server, user, password; org picker when several exist.
+ **Byt bolag** in the main menu reopens the picker during the session.
- **Dashboard** — status line with org, fiscal year, role and user.
- **Verifikat** — list and detail view (rows with column headers, an
underlag section separated by a rule, hash, link to corrected voucher); `c`
diff --git a/docs/STATE.md b/docs/STATE.md
index 9d954cd..31d6b14 100644
--- a/docs/STATE.md
+++ b/docs/STATE.md
@@ -14,6 +14,11 @@ unit tests and the docs consistency check.
## Resume here (2026-09-22)
+- **Byt bolag (2026-09-23, branch `eff/switch-org`)**: a main-menu item
+ reopens the org picker and switches org in the running session (context
+ reloaded, list selections cleared). `app_refresh_context` now clears the
+ previous org's name/role/fiscal year first, so an org without a readable
+ year no longer shows the old one.
- **Goto search (2026-09-23, branch `eff/goto-search`)**: `g` followed by a
letter searches the row texts of any list or menu (case-insensitive, åäö
too; up/down = next/previous match); a digit still jumps to a row number.
diff --git a/docs/TUI-GUIDELINES.md b/docs/TUI-GUIDELINES.md
index c2aff3d..68316e3 100644
--- a/docs/TUI-GUIDELINES.md
+++ b/docs/TUI-GUIDELINES.md
@@ -125,8 +125,12 @@ struct tui_action {
## Session start
After login the org picker ("Välj organisation att representera") is always
-shown; the selected org is fixed for the session (`--org ID` bypasses it for
-scripts). The fiscal year is chosen from the dashboard and is changeable
+shown (`--org ID` bypasses it for scripts). "Byt bolag" in the main menu
+opens the same picker, with the cursor on the current org, and switches the
+session to the chosen one: `session.use_org`, then the org's name, role,
+current fiscal year and series are reloaded and the remembered list
+selections are cleared; `Esc` keeps the current org. `Ctrl+R` keeps the
+switched org. The fiscal year is chosen from the dashboard and is changeable
during the session; the `Räkenskapsår` screen also closes and reopens years
there.
diff --git a/scripts/tui-golden.py b/scripts/tui-golden.py
index 71e1273..0dfde5f 100755
--- a/scripts/tui-golden.py
+++ b/scripts/tui-golden.py
@@ -41,6 +41,8 @@ from pathlib import Path
ORG_NAME = "Test AB"
ORG_NR = "5560123456"
+ORG2_NAME = "Andra AB"
+ORG2_NR = "5560654321"
DOWNLOAD_PATH = f"/tmp/bokf-golden-dl-{os.getpid()}.txt"
KEYS = {
@@ -618,6 +620,32 @@ SCENARIOS = [
],
},
{
+ # "Byt bolag" in the main menu switches org and its context (name,
+ # year); Esc in the picker keeps the current org.
+ "name": "switch-org",
+ "screen": "dashboard",
+ "expect": ["{org_name}", "Byt bolag", "Logga ut / avsluta"],
+ "steps": [
+ {
+ "keys": ["g", "byt", "enter", "enter"],
+ "expect": ["Välj organisation att representera",
+ "Andra AB"],
+ },
+ {
+ "keys": ["esc"],
+ "expect": ["{org_name} |", "Byt bolag"],
+ },
+ {
+ "keys": ["enter", "down", "enter"],
+ "expect": ["Andra AB |", "Byt bolag"],
+ },
+ {
+ "keys": ["1"],
+ "expect": ["sorterade på nummer", "Nytt verifikat"],
+ },
+ ],
+ },
+ {
# Backspace in a freshly opened field edits the value; the next
# key must not replace everything.
"name": "form-edit-backspace",
@@ -1207,6 +1235,11 @@ def setup_rig(repo, tmp, env):
"tax_table": 30,
"tax_column": 1,
"email": "test@example.com"})], env)
+ # a second org for "Byt bolag"; the TUI always starts in the first
+ run_checked([str(bind / "bokfctl"), "--socket", str(sock),
+ "--user", "admin", "--password", env["BOKFD_PASSWORD"],
+ "org.create",
+ json.dumps({"name": ORG2_NAME, "org_nr": ORG2_NR})], env)
return daemon, sock, org_id, fy