diff options
| author | Anders Betts <anders.betts@gmail.com> | 2026-09-21 21:43:44 +0200 |
|---|---|---|
| committer | Anders Betts <anders.betts@gmail.com> | 2026-09-21 21:43:44 +0200 |
| commit | 62a92f0af0442c78277c3c97c9efe6fef8145625 (patch) | |
| tree | 3802a62904880da22bf84a44e9ece3cdff9cd8aa /src | |
| parent | f1eeb15781a044a9451ee08b91c63feb395b4767 (diff) | |
| download | bokf-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 'src')
| -rw-r--r-- | src/cmd_bokslut.c | 9 | ||||
| -rw-r--r-- | src/cmd_invoices.c | 4 | ||||
| -rw-r--r-- | src/cmd_payroll.c | 8 | ||||
| -rw-r--r-- | src/cmd_settings.c | 44 | ||||
| -rw-r--r-- | src/db.c | 8 | ||||
| -rw-r--r-- | src/db.h | 3 | ||||
| -rw-r--r-- | src/ledger.c | 6 | ||||
| -rw-r--r-- | src/reports.c | 29 | ||||
| -rw-r--r-- | src/sie.c | 16 |
9 files changed, 106 insertions, 21 deletions
diff --git a/src/cmd_bokslut.c b/src/cmd_bokslut.c index c813923..f0ddee9 100644 --- a/src/cmd_bokslut.c +++ b/src/cmd_bokslut.c @@ -88,6 +88,9 @@ static yyjson_mut_val *h_bokslut_post(struct req *r) } int64_t result_before = 0; + char ib_series[16]; + db_setting_copy(r->db, r->org_id, "series_ib", "IB", ib_series, + sizeof ib_series); if (sqlite3_prepare_v2( r->db, "SELECT COALESCE(SUM(r.credit_ore)-SUM(r.debit_ore),0)" @@ -95,11 +98,13 @@ static yyjson_mut_val *h_bokslut_post(struct req *r) " JOIN vouchers v ON v.org_id=r.org_id AND v.id=r.voucher_id" " JOIN accounts a ON a.org_id=r.org_id AND a.id=r.account_id" " WHERE r.org_id=?1 AND v.fiscal_year_id=?2 AND v.series<>'IB'" + " AND v.series<>?3" " AND a.type IN ('revenue','expense') AND a.number NOT LIKE '89%'", -1, &st, NULL) != SQLITE_OK) return db_error(r); sqlite3_bind_int64(st, 1, r->org_id); sqlite3_bind_int64(st, 2, fy_id); + sqlite3_bind_text(st, 3, ib_series, -1, SQLITE_TRANSIENT); if (sqlite3_step(st) == SQLITE_ROW) result_before = sqlite3_column_int64(st, 0); sqlite3_finalize(st); @@ -154,6 +159,9 @@ static yyjson_mut_val *h_bokslut_post(struct req *r) plan[np++] = (typeof(plan[0])){ "Resultatdisposition", drows, 2 }; } + char series[16]; + db_setting_copy(r->db, r->org_id, "series_bokslut", "Å", series, + sizeof series); yyjson_mut_val *vouchers = yyjson_mut_arr(r->rdoc); for (size_t i = 0; i < np; i++) { struct ledger_post_opts o; @@ -165,6 +173,7 @@ static yyjson_mut_val *h_bokslut_post(struct req *r) o.description = plan[i].desc; o.rows = plan[i].rows; o.nrows = plan[i].nrows; + o.series = series; o.dry_run = r->dry_run; struct ledger_error e; char *json = NULL; diff --git a/src/cmd_invoices.c b/src/cmd_invoices.c index 0892ac8..73273dc 100644 --- a/src/cmd_invoices.c +++ b/src/cmd_invoices.c @@ -821,6 +821,9 @@ static yyjson_mut_val *h_invoice_issue(struct req *r) invoice_store_attachment(r, &v, pdf, pdf_len, &attachment_id) != 0) goto done; + char series[16]; + db_setting_copy(r->db, r->org_id, "series_invoice", "F", series, + sizeof series); struct ledger_post_opts o; memset(&o, 0, sizeof o); o.org_id = r->org_id; @@ -831,6 +834,7 @@ static yyjson_mut_val *h_invoice_issue(struct req *r) o.rows = vrows; o.nrows = vn; o.source = "invoice"; + o.series = series; o.dry_run = r->dry_run; o.already_in_tx = 1; struct ledger_error e; diff --git a/src/cmd_payroll.c b/src/cmd_payroll.c index b5db4f1..84ae323 100644 --- a/src/cmd_payroll.c +++ b/src/cmd_payroll.c @@ -491,6 +491,9 @@ static yyjson_mut_val *h_payroll_run_post(struct req *r) char description[64]; snprintf(description, sizeof description, "Lönekörning %s", period); + char series[16]; + db_setting_copy(r->db, r->org_id, "series_payroll", "L", series, + sizeof series); struct ledger_post_opts o; memset(&o, 0, sizeof o); o.org_id = r->org_id; @@ -501,6 +504,7 @@ static yyjson_mut_val *h_payroll_run_post(struct req *r) o.rows = vrows; o.nrows = vn; o.source = "payroll"; + o.series = series; o.dry_run = r->dry_run; o.already_in_tx = 1; struct ledger_error e; @@ -1341,6 +1345,9 @@ static yyjson_mut_val *h_payroll_pay_tax(struct req *r) if (db_exec(r->db, "BEGIN IMMEDIATE", NULL) != 0) return fail(r, "DB_BUSY", "could not start transaction"); in_tx = 1; + char series[16]; + db_setting_copy(r->db, r->org_id, "series_payroll", "L", series, + sizeof series); struct ledger_post_opts o; memset(&o, 0, sizeof o); o.org_id = r->org_id; @@ -1351,6 +1358,7 @@ static yyjson_mut_val *h_payroll_pay_tax(struct req *r) o.rows = rows; o.nrows = vn; o.source = "payroll_tax"; + o.series = series; o.dry_run = r->dry_run; o.already_in_tx = 1; if (ledger_post(r->db, &o, &e, &voucher_json) != 0) { diff --git a/src/cmd_settings.c b/src/cmd_settings.c index 066dc55..28167de 100644 --- a/src/cmd_settings.c +++ b/src/cmd_settings.c @@ -20,6 +20,9 @@ static yyjson_mut_val *h_settings_get(struct req *r) yyjson_mut_val *o = yyjson_mut_obj(r->rdoc); int have_default = 0, have_bank = 0, have_receivable = 0, have_revenue = 0; int have_password = 0, have_security = 0; + int have_voucher = 0, have_invoice = 0, have_payroll = 0; + int have_bokslut = 0, have_ib = 0; + char legacy_series[16] = "A"; sqlite3_stmt *st = NULL; if (sqlite3_prepare_v2( r->db, "SELECT key,value FROM settings WHERE org_id=?1", -1, &st, @@ -35,22 +38,46 @@ static yyjson_mut_val *h_settings_get(struct req *r) } yyjson_mut_obj_add(o, yyjson_mut_strcpy(r->rdoc, k), yyjson_mut_strcpy(r->rdoc, v ? v : "")); - if (strcmp(k, "default_series") == 0) + if (strcmp(k, "default_series") == 0) { have_default = 1; - if (strcmp(k, "bank_account") == 0) + if (v && *v) + snprintf(legacy_series, sizeof legacy_series, "%s", v); + } else if (strcmp(k, "series_voucher") == 0) { + have_voucher = 1; + } else if (strcmp(k, "series_invoice") == 0) { + have_invoice = 1; + } else if (strcmp(k, "series_payroll") == 0) { + have_payroll = 1; + } else if (strcmp(k, "series_bokslut") == 0) { + have_bokslut = 1; + } else if (strcmp(k, "series_ib") == 0) { + have_ib = 1; + } else if (strcmp(k, "bank_account") == 0) { have_bank = 1; - if (strcmp(k, "invoice_receivable_account") == 0) + } else if (strcmp(k, "invoice_receivable_account") == 0) { have_receivable = 1; - if (strcmp(k, "invoice_revenue_account") == 0) + } else if (strcmp(k, "invoice_revenue_account") == 0) { have_revenue = 1; - if (strcmp(k, "smtp_security") == 0) + } else if (strcmp(k, "smtp_security") == 0) { have_security = 1; + } } } sqlite3_finalize(st); } if (!have_default) yyjson_mut_obj_add_strcpy(r->rdoc, o, "default_series", "A"); + if (!have_voucher) + yyjson_mut_obj_add_strcpy(r->rdoc, o, "series_voucher", + legacy_series); + if (!have_invoice) + yyjson_mut_obj_add_strcpy(r->rdoc, o, "series_invoice", "F"); + if (!have_payroll) + yyjson_mut_obj_add_strcpy(r->rdoc, o, "series_payroll", "L"); + if (!have_bokslut) + yyjson_mut_obj_add_strcpy(r->rdoc, o, "series_bokslut", "Å"); + if (!have_ib) + yyjson_mut_obj_add_strcpy(r->rdoc, o, "series_ib", "IB"); if (!have_bank) yyjson_mut_obj_add_strcpy(r->rdoc, o, "bank_account", "1930"); if (!have_receivable) @@ -135,7 +162,12 @@ static yyjson_mut_val *h_settings_set(struct req *r) } size_t maxlen; int digits_only = 0, bankgiro = 0, port = 0, security = 0; - if (strcmp(key, "default_series") == 0) + if (strcmp(key, "default_series") == 0 || + strcmp(key, "series_voucher") == 0 || + strcmp(key, "series_invoice") == 0 || + strcmp(key, "series_payroll") == 0 || + strcmp(key, "series_bokslut") == 0 || + strcmp(key, "series_ib") == 0) maxlen = 8; else if (strcmp(key, "attachment_dir") == 0 || strcmp(key, "smtp_host") == 0 || @@ -1215,6 +1215,14 @@ char *db_setting(sqlite3 *db, int64_t org_id, const char *key) return value; } +void db_setting_copy(sqlite3 *db, int64_t org_id, const char *key, + const char *def, char *out, size_t n) +{ + char *v = db_setting(db, org_id, key); + snprintf(out, n, "%s", v && *v ? v : def); + free(v); +} + char *db_membership_role(sqlite3 *db, int64_t org_id, int64_t user_id) { sqlite3_stmt *st = NULL; @@ -2,6 +2,7 @@ #define BOKF_DB_H #include <sqlite3.h> +#include <stddef.h> #include <stdint.h> #define BOKF_SCHEMA_VERSION 11 @@ -14,6 +15,8 @@ int db_create_user(sqlite3 *db, const char *username, const char *display_name, char **err); char *db_membership_role(sqlite3 *db, int64_t org_id, int64_t user_id); char *db_setting(sqlite3 *db, int64_t org_id, const char *key); +void db_setting_copy(sqlite3 *db, int64_t org_id, const char *key, + const char *def, char *out, size_t n); int64_t db_count(sqlite3 *db, const char *sql); int64_t db_last_id(sqlite3 *db); diff --git a/src/ledger.c b/src/ledger.c index a22e15e..b9aad47 100644 --- a/src/ledger.c +++ b/src/ledger.c @@ -360,7 +360,11 @@ int ledger_post(sqlite3 *db, const struct ledger_post_opts *o, char *series_owned = NULL; const char *series = o->series; if (!series || !*series) { - series_owned = db_setting(db, o->org_id, "default_series"); + series_owned = db_setting(db, o->org_id, "series_voucher"); + if (!series_owned || !*series_owned) { + free(series_owned); + series_owned = db_setting(db, o->org_id, "default_series"); + } series = series_owned && *series_owned ? series_owned : "A"; } if (!util_parse_iso_date(o->date)) { diff --git a/src/reports.c b/src/reports.c index 1d4efdd..2ca7e45 100644 --- a/src/reports.c +++ b/src/reports.c @@ -4,6 +4,7 @@ #include <stdlib.h> #include <string.h> +#include "db.h" #include "util.h" struct fy_info { @@ -65,19 +66,22 @@ static yyjson_mut_val *balance_query(yyjson_mut_doc *doc, sqlite3 *db, const char *from, const char *to, const char *types_filter, char **err) { + char ib_series[16]; + db_setting_copy(db, org_id, "series_ib", "IB", ib_series, + sizeof ib_series); char sql[1280]; snprintf(sql, sizeof sql, "SELECT a.number,a.name,a.type," - " COALESCE(SUM(CASE WHEN v.series <> 'IB'" + " COALESCE(SUM(CASE WHEN v.series <> 'IB' AND v.series <> ?5" " AND v.date BETWEEN ?2 AND ?3 THEN r.debit_ore END),0)," - " COALESCE(SUM(CASE WHEN v.series <> 'IB'" + " COALESCE(SUM(CASE WHEN v.series <> 'IB' AND v.series <> ?5" " AND v.date BETWEEN ?2 AND ?3 THEN r.credit_ore END),0)," - " COALESCE(SUM(CASE WHEN (v.series = 'IB'" + " COALESCE(SUM(CASE WHEN ((v.series = 'IB' OR v.series = ?5)" " AND v.fiscal_year_id = ?4) OR (v.series <> 'IB'" - " AND v.date < ?2) THEN r.debit_ore END),0)," - " COALESCE(SUM(CASE WHEN (v.series = 'IB'" + " AND v.series <> ?5 AND v.date < ?2) THEN r.debit_ore END),0)," + " COALESCE(SUM(CASE WHEN ((v.series = 'IB' OR v.series = ?5)" " AND v.fiscal_year_id = ?4) OR (v.series <> 'IB'" - " AND v.date < ?2) THEN r.credit_ore END),0)" + " AND v.series <> ?5 AND v.date < ?2) THEN r.credit_ore END),0)" " FROM accounts a" " LEFT JOIN (voucher_rows r JOIN vouchers v" " ON v.org_id=r.org_id AND v.id=r.voucher_id)" @@ -94,6 +98,7 @@ static yyjson_mut_val *balance_query(yyjson_mut_doc *doc, sqlite3 *db, sqlite3_bind_text(st, 2, from, -1, SQLITE_TRANSIENT); sqlite3_bind_text(st, 3, to, -1, SQLITE_TRANSIENT); sqlite3_bind_int64(st, 4, fy_id); + sqlite3_bind_text(st, 5, ib_series, -1, SQLITE_TRANSIENT); yyjson_mut_val *arr = yyjson_mut_arr(doc); while (sqlite3_step(st) == SQLITE_ROW) { const char *number = (const char *)sqlite3_column_text(st, 0); @@ -377,6 +382,9 @@ yyjson_mut_val *report_vat(yyjson_mut_doc *doc, sqlite3 *db, int64_t org_id, int64_t amount; } acc[VAT_MAX_BOXES]; size_t nacc = 0; + char ib_series[16]; + db_setting_copy(db, org_id, "series_ib", "IB", ib_series, + sizeof ib_series); sqlite3_stmt *st = NULL; if (sqlite3_prepare_v2( db, @@ -401,21 +409,22 @@ yyjson_mut_val *report_vat(yyjson_mut_doc *doc, sqlite3 *db, int64_t org_id, " JOIN vouchers v ON v.org_id=r.org_id AND v.id=r.voucher_id" " JOIN accounts a ON a.org_id=r.org_id AND a.id=r.account_id" " WHERE r.org_id=?1 AND a.number=?2 AND v.series <> 'IB'" - " AND v.date BETWEEN ?3 AND ?4"; + " AND v.series <> ?5 AND v.date BETWEEN ?3 AND ?4"; else if (strcmp(mt, "type") == 0) sql = "SELECT COALESCE(SUM(r.debit_ore),0)," "COALESCE(SUM(r.credit_ore),0) FROM voucher_rows r" " JOIN vouchers v ON v.org_id=r.org_id AND v.id=r.voucher_id" " JOIN accounts a ON a.org_id=r.org_id AND a.id=r.account_id" " WHERE r.org_id=?1 AND a.type=?2 AND v.series <> 'IB'" - " AND v.date BETWEEN ?3 AND ?4"; + " AND v.series <> ?5 AND v.date BETWEEN ?3 AND ?4"; else sql = "SELECT COALESCE(SUM(r.debit_ore),0)," "COALESCE(SUM(r.credit_ore),0) FROM voucher_rows r" " JOIN vouchers v ON v.org_id=r.org_id AND v.id=r.voucher_id" " JOIN accounts a ON a.org_id=r.org_id AND a.id=r.account_id" " WHERE r.org_id=?1 AND CAST(a.number AS INTEGER) BETWEEN ?2" - " AND ?3 AND v.series <> 'IB' AND v.date BETWEEN ?4 AND ?5"; + " AND ?3 AND v.series <> 'IB' AND v.series <> ?6" + " AND v.date BETWEEN ?4 AND ?5"; if (sqlite3_prepare_v2(db, sql, -1, &qs, NULL) != SQLITE_OK) break; sqlite3_bind_int64(qs, 1, org_id); @@ -423,6 +432,7 @@ yyjson_mut_val *report_vat(yyjson_mut_doc *doc, sqlite3 *db, int64_t org_id, sqlite3_bind_text(qs, 2, pattern, -1, SQLITE_TRANSIENT); sqlite3_bind_text(qs, 3, from, -1, SQLITE_TRANSIENT); sqlite3_bind_text(qs, 4, to, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(qs, 5, ib_series, -1, SQLITE_TRANSIENT); } else { long lo = 0, hi = 0; if (sscanf(pattern, "%ld-%ld", &lo, &hi) != 2) { @@ -433,6 +443,7 @@ yyjson_mut_val *report_vat(yyjson_mut_doc *doc, sqlite3 *db, int64_t org_id, sqlite3_bind_int64(qs, 3, hi); sqlite3_bind_text(qs, 4, from, -1, SQLITE_TRANSIENT); sqlite3_bind_text(qs, 5, to, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(qs, 6, ib_series, -1, SQLITE_TRANSIENT); } if (sqlite3_step(qs) == SQLITE_ROW) amount = (sqlite3_column_int64(qs, 0) - @@ -8,6 +8,7 @@ #include <string.h> #include <time.h> +#include "db.h" #include "ledger.h" #include "log.h" #include "util.h" @@ -135,18 +136,22 @@ int sie_export_file(sqlite3 *db, int64_t org_id, int64_t fy_id, put_date(f, end); put(f, "\n"); - /* accounts and balances: IB = this year's series "IB" voucher(s) plus - all non-IB history before the year; UB = everything through the end; + /* accounts and balances: IB = this year's IB-series voucher(s) plus all + non-IB history before the year; UB = everything through the end; RES = the year's non-IB movements. */ + char ib_series[16]; + db_setting_copy(db, org_id, "series_ib", "IB", ib_series, + sizeof ib_series); if (sqlite3_prepare_v2( db, "SELECT a.number,a.name," - " COALESCE(SUM(CASE WHEN (v.series = 'IB'" + " COALESCE(SUM(CASE WHEN ((v.series = 'IB' OR v.series = ?5)" " AND v.fiscal_year_id = ?4) OR (v.series <> 'IB'" - " AND v.date < ?2) THEN r.debit_ore - r.credit_ore END),0)," + " AND v.series <> ?5 AND v.date < ?2)" + " THEN r.debit_ore - r.credit_ore END),0)," " COALESCE(SUM(CASE WHEN v.date <= ?3" " THEN r.debit_ore - r.credit_ore END),0)," - " COALESCE(SUM(CASE WHEN v.series <> 'IB'" + " COALESCE(SUM(CASE WHEN v.series <> 'IB' AND v.series <> ?5" " AND v.date >= ?2 AND v.date <= ?3" " THEN r.debit_ore - r.credit_ore END),0)" " FROM accounts a LEFT JOIN (voucher_rows r JOIN vouchers v" @@ -163,6 +168,7 @@ int sie_export_file(sqlite3 *db, int64_t org_id, int64_t fy_id, sqlite3_bind_text(st, 2, start, -1, SQLITE_TRANSIENT); sqlite3_bind_text(st, 3, end, -1, SQLITE_TRANSIENT); sqlite3_bind_int64(st, 4, fy_id); + sqlite3_bind_text(st, 5, ib_series, -1, SQLITE_TRANSIENT); while (sqlite3_step(st) == SQLITE_ROW) { const char *number = (const char *)sqlite3_column_text(st, 0); const char *name = (const char *)sqlite3_column_text(st, 1); |
