diff options
| author | Anders Betts <anders.betts@gmail.com> | 2026-09-19 20:13:02 +0200 |
|---|---|---|
| committer | Anders Betts <anders.betts@gmail.com> | 2026-09-19 20:13:02 +0200 |
| commit | 77c298b3b64f2493e509734df0084068ad24441f (patch) | |
| tree | 8a8182fbecdb0dc63cdc71842511cc6d00e21716 /src | |
| parent | 30c309e15d3211e7d0e8a8a2752c3e9afa0a0638 (diff) | |
| download | bokf-77c298b3b64f2493e509734df0084068ad24441f.tar.gz bokf-77c298b3b64f2493e509734df0084068ad24441f.zip | |
bokslut: TUI screen, K2 årsredovisning draft, storable dividend (schema v4)v0.1.33
Diffstat (limited to 'src')
| -rw-r--r-- | src/commands.c | 70 | ||||
| -rw-r--r-- | src/db.c | 14 | ||||
| -rw-r--r-- | src/db.h | 2 |
3 files changed, 84 insertions, 2 deletions
diff --git a/src/commands.c b/src/commands.c index f03e0e4..62de0be 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1696,10 +1696,12 @@ static yyjson_mut_val *fy_json(struct req *r, sqlite3_stmt *st) else yyjson_mut_obj_add_strcpy(r->rdoc, o, "locked_until", sq(sqlite3_column_text(st, 5))); + yyjson_mut_obj_add_int(r->rdoc, o, "dividend_ore", + sqlite3_column_int64(st, 6)); return o; } -#define FY_COLUMNS "id,label,start_date,end_date,status,locked_until" +#define FY_COLUMNS "id,label,start_date,end_date,status,locked_until,dividend_ore" static yyjson_mut_val *h_fiscal_year_list(struct req *r) { @@ -1878,6 +1880,70 @@ static yyjson_mut_val *h_fiscal_year_reopen(struct req *r) return yyjson_mut_obj(r->rdoc); } +/* Fiscal-year metadata that is not a posting: today only the board's + proposed dividend, kept for the årsredovisning draft. */ +static yyjson_mut_val *h_fiscal_year_update(struct req *r) +{ + int64_t id = 0, dividend = 0; + if (!arg_int(r->args, "id", &id) || id <= 0) + return fail(r, "INVALID_ARGS", "id is required"); + if (!arg_int(r->args, "dividend_ore", ÷nd)) + return fail(r, "INVALID_ARGS", "dividend_ore is required"); + if (dividend < 0) + return fail(r, "INVALID_ARGS", "dividend_ore must be >= 0"); + sqlite3_stmt *st = NULL; + if (sqlite3_prepare_v2(r->db, + "SELECT count(*) FROM fiscal_years" + " WHERE org_id=?1 AND id=?2", + -1, &st, NULL) != SQLITE_OK) + return fail(r, "INTERNAL", "database error"); + sqlite3_bind_int64(st, 1, r->org_id); + sqlite3_bind_int64(st, 2, id); + int64_t exists = 0; + if (sqlite3_step(st) == SQLITE_ROW) + exists = sqlite3_column_int64(st, 0); + sqlite3_finalize(st); + if (!exists) + return fail(r, "NOT_FOUND", "fiscal year not found"); + if (r->dry_run) { + yyjson_mut_val *o = yyjson_mut_obj(r->rdoc); + yyjson_mut_obj_add_bool(r->rdoc, o, "dry_run", true); + yyjson_mut_obj_add_int(r->rdoc, o, "id", id); + yyjson_mut_obj_add_int(r->rdoc, o, "dividend_ore", dividend); + return o; + } + if (sqlite3_prepare_v2(r->db, + "UPDATE fiscal_years SET dividend_ore=?3" + " WHERE org_id=?1 AND id=?2", + -1, &st, NULL) != SQLITE_OK) + return fail(r, "INTERNAL", "database error"); + sqlite3_bind_int64(st, 1, r->org_id); + sqlite3_bind_int64(st, 2, id); + sqlite3_bind_int64(st, 3, dividend); + int rc = sqlite3_step(st); + sqlite3_finalize(st); + if (rc != SQLITE_DONE) + return fail(r, "INTERNAL", sqlite3_errmsg(r->db)); + char *reqjson = audit_args_json(r->args); + audit_append(r->db, r->org_id, r->sess->user_id, r->sess->token_id, + "fiscal_year.update", reqjson, "OK", NULL); + free(reqjson); + if (sqlite3_prepare_v2(r->db, + "SELECT " FY_COLUMNS " FROM fiscal_years" + " WHERE org_id=?1 AND id=?2", + -1, &st, NULL) != SQLITE_OK) + return fail(r, "INTERNAL", "database error"); + sqlite3_bind_int64(st, 1, r->org_id); + sqlite3_bind_int64(st, 2, id); + yyjson_mut_val *o = NULL; + if (sqlite3_step(st) == SQLITE_ROW) + o = fy_json(r, st); + sqlite3_finalize(st); + if (!o) + return fail(r, "NOT_FOUND", "fiscal year not found"); + return o; +} + static yyjson_mut_val *h_period_lock(struct req *r) { int64_t fy = 0; @@ -4119,6 +4185,8 @@ const struct command g_commands[] = { h_fiscal_year_close }, { "fiscal_year.reopen", "Reopen a closed fiscal year", PERM_OWNER, 1, 1, 1, h_fiscal_year_reopen }, + { "fiscal_year.update", "Update fiscal-year metadata (dividend)", + PERM_WRITE, 1, 1, 1, h_fiscal_year_update }, { "period.lock", "Lock a period through a date", PERM_OWNER, 1, 1, 1, h_period_lock }, { "period.unlock", "Remove a period lock", PERM_OWNER, 1, 1, 1, @@ -96,6 +96,7 @@ static const char SCHEMA_V1[] = " end_date TEXT NOT NULL," " status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','closed'))," " locked_until TEXT," + " dividend_ore INTEGER NOT NULL DEFAULT 0," " created_at TEXT NOT NULL," " closed_at TEXT," " closed_by INTEGER REFERENCES users(id)," @@ -362,6 +363,15 @@ static int db_upgrade_v3(sqlite3 *db, char **err) return 0; } +/* v4: the årsredovisning keeps the board's proposed dividend per year. */ +static int db_upgrade_v4(sqlite3 *db, char **err) +{ + return db_exec(db, + "ALTER TABLE fiscal_years ADD COLUMN dividend_ore" + " INTEGER NOT NULL DEFAULT 0", + err); +} + static int db_upgrade(sqlite3 *db, int from, char **err) { if (db_exec(db, "BEGIN IMMEDIATE", err) != 0) @@ -374,6 +384,10 @@ static int db_upgrade(sqlite3 *db, int from, char **err) db_exec(db, "ROLLBACK", NULL); return -1; } + if (from < 4 && db_upgrade_v4(db, err) != 0) { + db_exec(db, "ROLLBACK", NULL); + return -1; + } char *sql = sqlite3_mprintf( "UPDATE meta SET value='%d' WHERE key='schema_version'", BOKF_SCHEMA_VERSION); @@ -4,7 +4,7 @@ #include <sqlite3.h> #include <stdint.h> -#define BOKF_SCHEMA_VERSION 3 +#define BOKF_SCHEMA_VERSION 4 int db_open(const char *path, sqlite3 **out, char **err); int db_migrate(sqlite3 *db, char **err); |
