aboutsummaryrefslogtreecommitdiff
path: root/src/reports.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reports.c')
-rw-r--r--src/reports.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/src/reports.c b/src/reports.c
index ee2bbe1..11c65f2 100644
--- a/src/reports.c
+++ b/src/reports.c
@@ -471,3 +471,232 @@ yyjson_mut_val *report_vat(yyjson_mut_doc *doc, sqlite3 *db, int64_t org_id,
" before filing");
return res;
}
+
+/* ---------------- huvudbok (general ledger) ---------------- */
+
+static int str_in_arr(yyjson_val *arr, const char *s)
+{
+ if (!arr)
+ return 1;
+ size_t n = yyjson_arr_size(arr);
+ for (size_t i = 0; i < n; i++) {
+ yyjson_val *v = yyjson_arr_get(arr, i);
+ if (v && yyjson_is_str(v) && strcmp(yyjson_get_str(v), s) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+static yyjson_mut_val *last_voucher_json(yyjson_mut_doc *doc, sqlite3 *db,
+ int64_t org_id, int64_t fy_id)
+{
+ sqlite3_stmt *st = NULL;
+ if (sqlite3_prepare_v2(
+ db,
+ "SELECT series,number FROM vouchers"
+ " WHERE org_id=?1 AND fiscal_year_id=?2"
+ " ORDER BY series DESC, number DESC LIMIT 1",
+ -1, &st, NULL) != SQLITE_OK)
+ return NULL;
+ sqlite3_bind_int64(st, 1, org_id);
+ sqlite3_bind_int64(st, 2, fy_id);
+ yyjson_mut_val *o = NULL;
+ if (sqlite3_step(st) == SQLITE_ROW) {
+ o = yyjson_mut_obj(doc);
+ yyjson_mut_obj_add_strcpy(doc, o, "series",
+ (const char *)sqlite3_column_text(st, 0));
+ yyjson_mut_obj_add_int(doc, o, "number", sqlite3_column_int64(st, 1));
+ }
+ sqlite3_finalize(st);
+ return o;
+}
+
+yyjson_mut_val *report_general_ledger(yyjson_mut_doc *doc, sqlite3 *db,
+ int64_t org_id, int64_t fy_id,
+ const char *from, const char *to,
+ yyjson_val *accounts, char **err)
+{
+ struct fy_info fy;
+ if (load_fy(db, org_id, fy_id, &fy, err) != 0)
+ return NULL;
+ if (!from)
+ from = fy.start;
+ if (!to)
+ to = fy.end;
+ yyjson_mut_val *bal =
+ balance_query(doc, db, org_id, fy.id, from, to, NULL, err);
+ if (!bal)
+ return NULL;
+ sqlite3_stmt *rs = NULL;
+ if (sqlite3_prepare_v2(
+ db,
+ "SELECT v.series,v.number,v.date,COALESCE(v.description,''),"
+ "r.debit_ore,r.credit_ore,COALESCE(r.description,'')"
+ " 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.number=?2 AND v.fiscal_year_id=?3"
+ " AND v.series <> 'IB' AND v.date BETWEEN ?4 AND ?5"
+ " ORDER BY v.date,v.series,v.number,r.id",
+ -1, &rs, NULL) != SQLITE_OK) {
+ set_err(err, "database error");
+ return NULL;
+ }
+ yyjson_mut_val *out = yyjson_mut_arr(doc);
+ size_t nbal = yyjson_mut_arr_size(bal);
+ for (size_t i = 0; i < nbal; i++) {
+ yyjson_mut_val *row = yyjson_mut_arr_get(bal, i);
+ const char *acc =
+ yyjson_mut_get_str(yyjson_mut_obj_get(row, "account"));
+ if (!acc || !*acc || !str_in_arr(accounts, acc))
+ continue;
+ int64_t ib = yyjson_mut_get_int(yyjson_mut_obj_get(row, "ib_ore"));
+ int64_t d = yyjson_mut_get_int(yyjson_mut_obj_get(row, "debit_ore"));
+ int64_t c = yyjson_mut_get_int(yyjson_mut_obj_get(row, "credit_ore"));
+ if (ib == 0 && d == 0 && c == 0)
+ continue;
+ const char *name =
+ yyjson_mut_get_str(yyjson_mut_obj_get(row, "name"));
+ yyjson_mut_val *ao = yyjson_mut_arr_add_obj(doc, out);
+ yyjson_mut_obj_add_strcpy(doc, ao, "account", acc);
+ yyjson_mut_obj_add_strcpy(doc, ao, "name", name ? name : "");
+ yyjson_mut_obj_add_int(doc, ao, "ib_ore", ib);
+ yyjson_mut_val *rows = yyjson_mut_arr(doc);
+ int64_t saldo = ib;
+ sqlite3_bind_int64(rs, 1, org_id);
+ sqlite3_bind_text(rs, 2, acc, -1, SQLITE_TRANSIENT);
+ sqlite3_bind_int64(rs, 3, fy.id);
+ sqlite3_bind_text(rs, 4, from, -1, SQLITE_TRANSIENT);
+ sqlite3_bind_text(rs, 5, to, -1, SQLITE_TRANSIENT);
+ while (sqlite3_step(rs) == SQLITE_ROW) {
+ int64_t rd = sqlite3_column_int64(rs, 4);
+ int64_t rc = sqlite3_column_int64(rs, 5);
+ saldo += rd - rc;
+ yyjson_mut_val *ro = yyjson_mut_arr_add_obj(doc, rows);
+ yyjson_mut_obj_add_strcpy(
+ doc, ro, "series",
+ (const char *)sqlite3_column_text(rs, 0));
+ yyjson_mut_obj_add_int(doc, ro, "number",
+ sqlite3_column_int64(rs, 1));
+ yyjson_mut_obj_add_strcpy(
+ doc, ro, "date",
+ (const char *)sqlite3_column_text(rs, 2));
+ yyjson_mut_obj_add_strcpy(
+ doc, ro, "description",
+ (const char *)sqlite3_column_text(rs, 3));
+ yyjson_mut_obj_add_int(doc, ro, "debit_ore", rd);
+ yyjson_mut_obj_add_int(doc, ro, "credit_ore", rc);
+ yyjson_mut_obj_add_strcpy(
+ doc, ro, "row_description",
+ (const char *)sqlite3_column_text(rs, 6));
+ yyjson_mut_obj_add_int(doc, ro, "saldo_ore", saldo);
+ }
+ sqlite3_reset(rs);
+ yyjson_mut_obj_add_val(doc, ao, "rows", rows);
+ yyjson_mut_obj_add_int(doc, ao, "debit_ore", d);
+ yyjson_mut_obj_add_int(doc, ao, "credit_ore", c);
+ yyjson_mut_obj_add_int(doc, ao, "ub_ore", ib + d - c);
+ }
+ sqlite3_finalize(rs);
+
+ yyjson_mut_val *o = yyjson_mut_obj(doc);
+ yyjson_mut_obj_add_val(doc, o, "fiscal_year", fy_json(doc, &fy));
+ yyjson_mut_obj_add_strcpy(doc, o, "from", from);
+ yyjson_mut_obj_add_strcpy(doc, o, "to", to);
+ yyjson_mut_val *lv = last_voucher_json(doc, db, org_id, fy_id);
+ if (lv)
+ yyjson_mut_obj_add_val(doc, o, "last_voucher", lv);
+ yyjson_mut_obj_add_val(doc, o, "accounts", out);
+ return o;
+}
+
+/* ---------------- verifikationslista (voucher list) ---------------- */
+
+yyjson_mut_val *report_voucher_list(yyjson_mut_doc *doc, sqlite3 *db,
+ int64_t org_id, int64_t fy_id,
+ const char *series, char **err)
+{
+ struct fy_info fy;
+ if (load_fy(db, org_id, fy_id, &fy, err) != 0)
+ return NULL;
+ char sql[256];
+ snprintf(sql, sizeof sql,
+ "SELECT id,series,number,date,COALESCE(description,'')"
+ " FROM vouchers WHERE org_id=?1 AND fiscal_year_id=?2%s"
+ " ORDER BY series,number",
+ series ? " AND series=?3" : "");
+ sqlite3_stmt *st = NULL;
+ if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) {
+ set_err(err, "database error");
+ return NULL;
+ }
+ sqlite3_bind_int64(st, 1, org_id);
+ sqlite3_bind_int64(st, 2, fy.id);
+ if (series)
+ sqlite3_bind_text(st, 3, series, -1, SQLITE_TRANSIENT);
+ sqlite3_stmt *rs = NULL;
+ if (sqlite3_prepare_v2(
+ db,
+ "SELECT a.number,a.name,r.debit_ore,r.credit_ore,"
+ "COALESCE(r.description,'') FROM voucher_rows r"
+ " JOIN accounts a ON a.org_id=r.org_id AND a.id=r.account_id"
+ " WHERE r.org_id=?1 AND r.voucher_id=?2 ORDER BY r.id",
+ -1, &rs, NULL) != SQLITE_OK) {
+ sqlite3_finalize(st);
+ set_err(err, "database error");
+ return NULL;
+ }
+ yyjson_mut_val *vouchers = yyjson_mut_arr(doc);
+ int64_t t_debit = 0, t_credit = 0;
+ while (sqlite3_step(st) == SQLITE_ROW) {
+ int64_t vid = sqlite3_column_int64(st, 0);
+ yyjson_mut_val *vo = yyjson_mut_arr_add_obj(doc, vouchers);
+ yyjson_mut_obj_add_int(doc, vo, "id", vid);
+ yyjson_mut_obj_add_strcpy(doc, vo, "series",
+ (const char *)sqlite3_column_text(st, 1));
+ yyjson_mut_obj_add_int(doc, vo, "number", sqlite3_column_int64(st, 2));
+ yyjson_mut_obj_add_strcpy(doc, vo, "date",
+ (const char *)sqlite3_column_text(st, 3));
+ yyjson_mut_obj_add_strcpy(doc, vo, "description",
+ (const char *)sqlite3_column_text(st, 4));
+ yyjson_mut_val *vrows = yyjson_mut_arr(doc);
+ sqlite3_bind_int64(rs, 1, org_id);
+ sqlite3_bind_int64(rs, 2, vid);
+ while (sqlite3_step(rs) == SQLITE_ROW) {
+ yyjson_mut_val *ro = yyjson_mut_arr_add_obj(doc, vrows);
+ yyjson_mut_obj_add_strcpy(
+ doc, ro, "account",
+ (const char *)sqlite3_column_text(rs, 0));
+ yyjson_mut_obj_add_strcpy(
+ doc, ro, "name",
+ (const char *)sqlite3_column_text(rs, 1));
+ int64_t rd = sqlite3_column_int64(rs, 2);
+ int64_t rc = sqlite3_column_int64(rs, 3);
+ yyjson_mut_obj_add_int(doc, ro, "debit_ore", rd);
+ yyjson_mut_obj_add_int(doc, ro, "credit_ore", rc);
+ yyjson_mut_obj_add_strcpy(
+ doc, ro, "description",
+ (const char *)sqlite3_column_text(rs, 4));
+ t_debit += rd;
+ t_credit += rc;
+ }
+ sqlite3_reset(rs);
+ yyjson_mut_obj_add_val(doc, vo, "rows", vrows);
+ }
+ sqlite3_finalize(rs);
+ sqlite3_finalize(st);
+
+ yyjson_mut_val *o = yyjson_mut_obj(doc);
+ yyjson_mut_obj_add_val(doc, o, "fiscal_year", fy_json(doc, &fy));
+ yyjson_mut_obj_add_strcpy(doc, o, "from", fy.start);
+ yyjson_mut_obj_add_strcpy(doc, o, "to", fy.end);
+ yyjson_mut_val *lv = last_voucher_json(doc, db, org_id, fy_id);
+ if (lv)
+ yyjson_mut_obj_add_val(doc, o, "last_voucher", lv);
+ yyjson_mut_obj_add_val(doc, o, "vouchers", vouchers);
+ yyjson_mut_val *tot = yyjson_mut_obj(doc);
+ yyjson_mut_obj_add_int(doc, tot, "debit_ore", t_debit);
+ yyjson_mut_obj_add_int(doc, tot, "credit_ore", t_credit);
+ yyjson_mut_obj_add_val(doc, o, "totals", tot);
+ return o;
+}