aboutsummaryrefslogtreecommitdiff
path: root/src/sru.c
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-22 22:48:14 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-22 22:48:14 +0200
commitca267e6b1c727be254c6229fc1ed3e2c5a0d6065 (patch)
tree1182e2d8efa3c0c8f1103e841132230341b83cfa /src/sru.c
parentfd08102a4be4e1f26c7afe005842002da923b987 (diff)
downloadbokf-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/sru.c')
-rw-r--r--src/sru.c66
1 files changed, 49 insertions, 17 deletions
diff --git a/src/sru.c b/src/sru.c
index 49916dc..8634c68 100644
--- a/src/sru.c
+++ b/src/sru.c
@@ -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;