summaryrefslogtreecommitdiff
path: root/clients
diff options
context:
space:
mode:
Diffstat (limited to 'clients')
-rw-r--r--clients/screens_bank.c76
-rw-r--r--clients/screens_vouchers.c28
-rw-r--r--clients/ui.h9
3 files changed, 106 insertions, 7 deletions
diff --git a/clients/screens_bank.c b/clients/screens_bank.c
index 63651bf..5131055 100644
--- a/clients/screens_bank.c
+++ b/clients/screens_bank.c
@@ -41,6 +41,7 @@ struct bank_sugg {
struct bank_tx {
int64_t id;
+ char account[16];
char booked_at[16];
char text[256];
int64_t amount_ore;
@@ -101,10 +102,56 @@ static void bank_tx_row(const struct bank_tx *t, char *line, size_t cap)
t->booked_at, text, amt, tail);
}
+/* Serie+nummer for a posted voucher, via voucher.get. */
+static void bank_voucher_ver(struct app *a, int64_t vid, char *buf, size_t n)
+{
+ buf[0] = '\0';
+ char args[64];
+ snprintf(args, sizeof args, "{\"id\":%lld}", (long long)vid);
+ char *resp = client_rpc(&a->conn, "voucher.get", a->session, a->org, args);
+ if (resp && client_ok(resp)) {
+ char *ser = jstr_dup(resp, "result.series");
+ bank_ver(buf, n, ser ? ser : "", jint_val(resp, "result.number", 0));
+ free(ser);
+ }
+ free(resp);
+}
+
+/* Prefill Nytt verifikat from the transaction, then match the posted voucher.
+ Returns 1 when a voucher was posted (matched or not), 0 on cancel. */
+static int bank_new_voucher(struct app *a, const struct bank_tx *t)
+{
+ struct voucher_prefill p;
+ memset(&p, 0, sizeof p);
+ p.date = t->booked_at;
+ p.description = t->text;
+ p.bank_account = t->account;
+ p.amount_ore = t->amount_ore;
+ int64_t vid = vouchers_new_prefill(a, &p);
+ if (vid <= 0)
+ return 0;
+ char args[64];
+ snprintf(args, sizeof args,
+ "{\"transaction_id\":%lld,\"voucher_id\":%lld}",
+ (long long)t->id, (long long)vid);
+ char *resp = client_rpc(&a->conn, "bank.match", a->session, a->org, args);
+ if (!resp || !client_ok(resp)) {
+ show_error("Bankavstämning", resp);
+ free(resp);
+ return 1;
+ }
+ free(resp);
+ char ver[32];
+ bank_voucher_ver(a, vid, ver, sizeof ver);
+ tui_message("Bankavstämning", "Matchat med %s.", ver);
+ return 1;
+}
+
/* Enter on an unmatched row: suggest vouchers, or pick one freely. */
static void bank_match_tx(struct app *a, const struct bank_tx *t)
{
- char **items = xcalloc(t->nsuggestions + 1, sizeof(char *));
+ size_t nact = t->nsuggestions + 2;
+ char **items = xcalloc(nact, sizeof(char *));
for (size_t i = 0; i < t->nsuggestions; i++) {
const struct bank_sugg *s = &t->suggestions[i];
char ver[32], amt[48], diff[48], line[256];
@@ -116,14 +163,18 @@ static void bank_match_tx(struct app *a, const struct bank_tx *t)
items[i] = xstrdup(line);
}
items[t->nsuggestions] = xstrdup("Välj annat verifikat…");
- int sel = tui_select_list("Matcha transaktion", items,
- (int)t->nsuggestions + 1, 0, 0, NULL, 0, NULL,
- 0);
- for (size_t i = 0; i <= t->nsuggestions; i++)
+ items[t->nsuggestions + 1] = xstrdup("Skapa nytt verifikat… (^N)");
+ int sel = tui_select_list("Matcha transaktion", items, (int)nact, 0, 0,
+ NULL, 0, NULL, 0);
+ for (size_t i = 0; i < nact; i++)
free(items[i]);
free(items);
if (sel < 0)
return;
+ if ((size_t)sel == nact - 1) {
+ bank_new_voucher(a, t);
+ return;
+ }
int64_t vid;
if ((size_t)sel < t->nsuggestions) {
vid = t->suggestions[sel].voucher_id;
@@ -278,6 +329,8 @@ void bank_screen(struct app *a)
for (size_t i = 0; i < n; i++) {
yyjson_val *it = yyjson_arr_get(items, i);
txs[i].id = vint(it, "id");
+ snprintf(txs[i].account, sizeof txs[i].account, "%s",
+ vstr(it, "account"));
snprintf(txs[i].booked_at, sizeof txs[i].booked_at, "%s",
vstr(it, "booked_at"));
snprintf(txs[i].text, sizeof txs[i].text, "%s", vstr(it, "text"));
@@ -333,7 +386,7 @@ void bank_screen(struct app *a)
size_t rows = n > 0 ? n : 1;
char **lines = xcalloc(rows, sizeof(char *));
if (n == 0) {
- lines[0] = xstrdup("+ Importera bankfil (^N)");
+ lines[0] = xstrdup("+ Importera bankfil (a)");
} else {
for (size_t i = 0; i < n; i++) {
char line[512];
@@ -354,7 +407,16 @@ void bank_screen(struct app *a)
yyjson_doc_free(doc);
return;
}
- if (sel >= 0 && (size_t)sel < n) {
+ if (sel == -4) {
+ if (n > 0 && ctx.cursor >= 0 && (size_t)ctx.cursor < n) {
+ keep_id = txs[ctx.cursor].id;
+ if (txs[ctx.cursor].nmatches == 0)
+ bank_new_voucher(a, &txs[ctx.cursor]);
+ else
+ tui_message("Bankavstämning",
+ "Transaktionen är redan matchad.");
+ }
+ } else if (sel >= 0 && (size_t)sel < n) {
keep_id = txs[sel].id;
if (txs[sel].nmatches == 0)
bank_match_tx(a, &txs[sel]);
diff --git a/clients/screens_vouchers.c b/clients/screens_vouchers.c
index d87cfd2..fedffe9 100644
--- a/clients/screens_vouchers.c
+++ b/clients/screens_vouchers.c
@@ -769,6 +769,11 @@ static int vform_key(void *ud, int ch)
int64_t vouchers_new(struct app *a)
{
+ return vouchers_new_prefill(a, NULL);
+}
+
+int64_t vouchers_new_prefill(struct app *a, const struct voucher_prefill *p)
+{
struct vform vf;
memset(&vf, 0, sizeof vf);
vf.a = a;
@@ -778,6 +783,28 @@ int64_t vouchers_new(struct app *a)
char *ref = util_random_id("tui-", 8);
snprintf(vf.client_ref, sizeof vf.client_ref, "%s", ref);
free(ref);
+ if (p) {
+ if (p->date && *p->date && util_parse_iso_date(p->date))
+ snprintf(vf.date, sizeof vf.date, "%s", p->date);
+ if (p->description && *p->description)
+ snprintf(vf.desc, sizeof vf.desc, "%s", p->description);
+ if (p->bank_account && *p->bank_account) {
+ snprintf(vf.rows[0].account, sizeof vf.rows[0].account, "%s",
+ p->bank_account);
+ int64_t amt = p->amount_ore < 0 ? -p->amount_ore : p->amount_ore;
+ char tmp[32];
+ tui_kr_format(amt, tmp, sizeof tmp);
+ if (p->amount_ore > 0)
+ snprintf(vf.rows[0].debit, sizeof vf.rows[0].debit, "%s",
+ tmp);
+ else if (p->amount_ore < 0)
+ snprintf(vf.rows[0].credit, sizeof vf.rows[0].credit, "%s",
+ tmp);
+ }
+ if (p->description && *p->description)
+ snprintf(vf.rows[0].text, sizeof vf.rows[0].text, "%s",
+ p->description);
+ }
int text_w = COLS - 60;
if (text_w < 8)
text_w = 8;
@@ -806,6 +833,7 @@ int64_t vouchers_new(struct app *a)
ff[2].cap = sizeof vf.desc;
ff[2].kind = TUI_F_TEXT;
tui_rt_set_fields(&vf.rt, ff, 3);
+ tui_rt_normalize(&vf.rt);
const char *hint = "Enter = ändra fält Tab = byta fält F4 = mall"
" ^F = bifoga fil F5 = validera ^X = rensa rad"
" ^Enter = bokför Esc = avbryt";
diff --git a/clients/ui.h b/clients/ui.h
index 1737259..d10f680 100644
--- a/clients/ui.h
+++ b/clients/ui.h
@@ -99,10 +99,19 @@ extern const struct is_group IS_GROUPS[IS_GROUPS_N];
void open_scene(struct app *a, const char *name);
void config_mkdirs(const char *path);
+/* Optional prefill for the new-voucher form (bank reconciliation). */
+struct voucher_prefill {
+ const char *date; /* YYYY-MM-DD, may be NULL */
+ const char *description; /* voucher text, may be NULL */
+ const char *bank_account; /* account number for the prefilled row */
+ int64_t amount_ore; /* > 0 debits the bank account, < 0 credits it */
+};
+
/* screen entry points (scene registry) */
void dashboard(struct app *a);
void update_status(struct app *a);
int64_t vouchers_new(struct app *a);
+int64_t vouchers_new_prefill(struct app *a, const struct voucher_prefill *p);
void vouchers_screen(struct app *a);
void acct_cache_free(void);
const char *acct_name(struct app *a, const char *number);