diff options
| author | Anders Betts <anders.betts@gmail.com> | 2026-09-22 22:48:14 +0200 |
|---|---|---|
| committer | Anders Betts <anders.betts@gmail.com> | 2026-09-22 22:48:14 +0200 |
| commit | ca267e6b1c727be254c6229fc1ed3e2c5a0d6065 (patch) | |
| tree | 1182e2d8efa3c0c8f1103e841132230341b83cfa /src | |
| parent | fd08102a4be4e1f26c7afe005842002da923b987 (diff) | |
| download | bokf-ca267e6b1c727be254c6229fc1ed3e2c5a0d6065.tar.gz bokf-ca267e6b1c727be254c6229fc1ed3e2c5a0d6065.zip | |
sru, tui: round every account to whole kronor; fix the årsredovisning Stäng skipv0.1.67
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/reports.c | 5 | ||||
| -rw-r--r-- | src/sru.c | 66 | ||||
| -rw-r--r-- | src/util.c | 5 | ||||
| -rw-r--r-- | src/util.h | 5 |
4 files changed, 63 insertions, 18 deletions
diff --git a/src/reports.c b/src/reports.c index 981d02c..265f2c2 100644 --- a/src/reports.c +++ b/src/reports.c @@ -574,7 +574,7 @@ yyjson_mut_val *report_general_ledger(yyjson_mut_doc *doc, sqlite3 *db, if (sqlite3_prepare_v2( db, "SELECT v.series,v.number,v.date,COALESCE(v.description,'')," - "r.debit_ore,r.credit_ore,COALESCE(r.description,'')" + "r.debit_ore,r.credit_ore,COALESCE(r.description,''),v.source" " 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" @@ -632,6 +632,9 @@ yyjson_mut_val *report_general_ledger(yyjson_mut_doc *doc, sqlite3 *db, yyjson_mut_obj_add_strcpy( doc, ro, "row_description", (const char *)sqlite3_column_text(rs, 6)); + yyjson_mut_obj_add_strcpy( + doc, ro, "source", + (const char *)sqlite3_column_text(rs, 7)); yyjson_mut_obj_add_int(doc, ro, "saldo_ore", saldo); } sqlite3_reset(rs); @@ -239,9 +239,15 @@ static int in_list(const char *const *list, size_t n, const char *code) return 0; } +/* Every account is rounded to whole kronor before it is added to its field, + so fields and totals agree to the krona with the annual report. 2099 is + left out: the equity carries the year's result from the income statement + instead, as the annual report does. *exact and *rounded (optional) sum the + mapped amounts before and after rounding. */ static void map_balance(yyjson_mut_val *arr, const struct bmap *tab, size_t ntab, struct fldset *out, - struct fldset *unmapped) + struct fldset *unmapped, int64_t *exact, + int64_t *rounded) { size_t n = yyjson_mut_arr_size(arr); for (size_t i = 0; i < n; i++) { @@ -251,6 +257,8 @@ static void map_balance(yyjson_mut_val *arr, const struct bmap *tab, if (!acc || !ore) continue; int num = atoi(acc); + if (num == 2099) + continue; const char *code = NULL; for (size_t t = 0; t < ntab; t++) { if (num >= tab[t].lo && num <= tab[t].hi) { @@ -258,16 +266,23 @@ static void map_balance(yyjson_mut_val *arr, const struct bmap *tab, break; } } - if (code) - fld_add(out, code, ore); - else + if (!code) { fld_add(unmapped, acc, ore); + continue; + } + int64_t kr = util_round_kr(ore); + fld_add(out, code, kr); + if (exact) + *exact += ore; + if (rounded) + *rounded += kr; } } +/* Returns the rounded total; *exact gets the unrounded one. */ static int64_t map_income(yyjson_mut_val *arr, const struct imap *tab, size_t ntab, struct fldset *out, - struct fldset *unmapped) + struct fldset *unmapped, int64_t *exact) { int64_t total = 0; size_t n = yyjson_mut_arr_size(arr); @@ -291,13 +306,15 @@ static int64_t map_income(yyjson_mut_val *arr, const struct imap *tab, fld_add(unmapped, acc, ore); continue; } + int64_t kr = util_round_kr(ore); if (strcmp(m->plus, m->minus) == 0) - fld_add(out, m->plus, ore); - else if (ore >= 0) - fld_add(out, m->plus, ore); + fld_add(out, m->plus, kr); + else if (kr >= 0) + fld_add(out, m->plus, kr); else - fld_add(out, m->minus, -ore); - total += ore; + fld_add(out, m->minus, -kr); + total += kr; + *exact += ore; } return total; } @@ -466,28 +483,43 @@ yyjson_mut_val *sru_export(yyjson_mut_doc *doc, sqlite3 *db, int64_t org_id, struct fldset ink2r = { 0 }, ink2s = { 0 }, ink2 = { 0 }; struct fldset unmapped = { 0 }; + int64_t a_exact = 0, a_kr = 0, el_exact = 0, el_kr = 0; map_balance(yyjson_mut_obj_get(yyjson_mut_obj_get(bal, "assets"), "accounts"), BAL_ASSETS, sizeof BAL_ASSETS / sizeof BAL_ASSETS[0], &ink2r, - &unmapped); + &unmapped, &a_exact, &a_kr); map_balance(yyjson_mut_obj_get(yyjson_mut_obj_get(bal, "equity"), "accounts"), - BAL_EL, sizeof BAL_EL / sizeof BAL_EL[0], &ink2r, &unmapped); + BAL_EL, sizeof BAL_EL / sizeof BAL_EL[0], &ink2r, &unmapped, + &el_exact, &el_kr); map_balance(yyjson_mut_obj_get(yyjson_mut_obj_get(bal, "liabilities"), "accounts"), - BAL_EL, sizeof BAL_EL / sizeof BAL_EL[0], &ink2r, &unmapped); + BAL_EL, sizeof BAL_EL / sizeof BAL_EL[0], &ink2r, &unmapped, + &el_exact, &el_kr); + int64_t rev_exact = 0, exp_exact = 0; int64_t rev_total = map_income( yyjson_mut_obj_get(yyjson_mut_obj_get(inc, "revenue"), "accounts"), - INC_REV, sizeof INC_REV / sizeof INC_REV[0], &ink2r, &unmapped); + INC_REV, sizeof INC_REV / sizeof INC_REV[0], &ink2r, &unmapped, + &rev_exact); int64_t exp_total = map_income( yyjson_mut_obj_get(yyjson_mut_obj_get(inc, "expenses"), "accounts"), - INC_EXP, sizeof INC_EXP / sizeof INC_EXP[0], &ink2r, &unmapped); + INC_EXP, sizeof INC_EXP / sizeof INC_EXP[0], &ink2r, &unmapped, + &exp_exact); int64_t result = rev_total - exp_total; if (result > 0) fld_add(&ink2r, "7450", result); else if (result < 0) fld_add(&ink2r, "7550", -result); + /* Fritt eget kapital carries the year's result; the rounding difference + (never a real imbalance) goes there too so the balance sheet balances + exactly like the annual report's. */ + int64_t res_exact = rev_exact - exp_exact; + int64_t plug = (a_kr - el_kr - result) - + util_round_kr(a_exact - el_exact - res_exact); + if (result || plug) + fld_add(&ink2r, "7302", result + plug); + if (unmapped.n > 0) { int ignore = 0; yyjson_val *iv = yyjson_obj_get(args, "ignore_unmapped"); @@ -517,7 +549,7 @@ yyjson_mut_val *sru_export(yyjson_mut_doc *doc, sqlite3 *db, int64_t org_id, fld_add(&ink2s, "7750", -result_kr * 100); int64_t tax = fld_get(&ink2r, "7528"); if (tax) - fld_add(&ink2s, "7651", (tax / 100) * 100); + fld_add(&ink2s, "7651", tax); yyjson_val *adj = yyjson_obj_get(args, "adjustments"); if (adj) { if (!yyjson_is_arr(adj)) { @@ -537,7 +569,7 @@ yyjson_mut_val *sru_export(yyjson_mut_doc *doc, sqlite3 *db, int64_t org_id, set_err(err, "adjustments: unknown INK2S field code"); return NULL; } - fld_add(&ink2s, code, (ore / 100) * 100); + fld_add(&ink2s, code, util_round_kr(ore)); } } int64_t overskott = 0; @@ -575,3 +575,8 @@ size_t util_utf8_to_cp437(const char *in, unsigned char *out, size_t out_sz) } return o; } + +int64_t util_round_kr(int64_t ore) +{ + return ore >= 0 ? (ore + 50) / 100 * 100 : -((-ore + 50) / 100 * 100); +} @@ -47,6 +47,11 @@ void util_date_add_months(const char *date, int months, char *out, size_t n); void util_date_add_days(const char *date, int days, char *out, size_t n); int util_date_valid(const char *s); +/* Öre rounded to whole kronor (half away from zero), still in öre. The + annual report and the SRU export round every account with it and sum the + results, so their lines, totals and fields agree to the krona. */ +int64_t util_round_kr(int64_t ore); + /* encoding conversion for SIE files (IBM PC8 / CP437) */ size_t util_utf8_to_cp437(const char *in, unsigned char *out, size_t out_sz); char *util_cp437_to_utf8(const unsigned char *in, size_t n); |
