aboutsummaryrefslogtreecommitdiff
path: root/clients/screens_rules.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/screens_rules.c')
-rw-r--r--clients/screens_rules.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/clients/screens_rules.c b/clients/screens_rules.c
new file mode 100644
index 0000000..19f898d
--- /dev/null
+++ b/clients/screens_rules.c
@@ -0,0 +1,226 @@
+#include <dirent.h>
+#include <errno.h>
+#include <locale.h>
+#include <math.h>
+#include <ncursesw/ncurses.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "client.h"
+#include "tui.h"
+#include "formula.h"
+#include "util.h"
+#include "version.h"
+#include "yyjson.h"
+#include "ui.h"
+
+/* ------------------------------------------------------------------ */
+/* momsregler (the vat report's report_rules) */
+/* ------------------------------------------------------------------ */
+
+static const char *const RULE_TYPES[] = { "account", "range", "type" };
+#define RULE_NTYPES ((int)(sizeof RULE_TYPES / sizeof RULE_TYPES[0]))
+static const char *const RULE_SIGNS[] = { "+", "-" };
+#define RULE_NSIGNS ((int)(sizeof RULE_SIGNS / sizeof RULE_SIGNS[0]))
+
+struct rule {
+ int64_t id;
+ char box[16];
+ char match_type[16];
+ char pattern[128];
+ int sign;
+ int64_t sort_order;
+};
+
+/* F5 dry-runs; Ctrl+Enter creates/updates. Returns 1 when saved. */
+static int rule_save(struct app *a, int64_t id, const char *box, int mti,
+ const char *pattern, int signi, const char *sort, int dry)
+{
+ char *end = NULL;
+ long long sort_order = strtoll(sort, &end, 10);
+ if (!sort[0] || !end || *end) {
+ tui_message("Momsregel", "Sortering måste vara ett heltal.");
+ return 0;
+ }
+ yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
+ yyjson_mut_val *o = yyjson_mut_obj(d);
+ yyjson_mut_doc_set_root(d, o);
+ if (id)
+ yyjson_mut_obj_add_int(d, o, "id", id);
+ yyjson_mut_obj_add_strcpy(d, o, "report", "vat");
+ yyjson_mut_obj_add_strcpy(d, o, "box", box);
+ yyjson_mut_obj_add_strcpy(d, o, "match_type", RULE_TYPES[mti]);
+ yyjson_mut_obj_add_strcpy(d, o, "pattern", pattern);
+ yyjson_mut_obj_add_int(d, o, "sign", signi == 1 ? -1 : 1);
+ yyjson_mut_obj_add_int(d, o, "sort_order", (int64_t)sort_order);
+ char *args = yyjson_mut_write(d, 0, NULL);
+ yyjson_mut_doc_free(d);
+ const char *cmd = id ? "report.rule_update" : "report.rule_create";
+ char *resp = dry ? rpc_dry(a, cmd, args)
+ : client_rpc(&a->conn, cmd, a->session, a->org, args);
+ free(args);
+ if (resp && client_ok(resp)) {
+ if (dry) {
+ tui_message("Validering OK", "Regeln är korrekt.");
+ } else {
+ tui_message("Momsregler", "Regeln sparad.");
+ free(resp);
+ return 1;
+ }
+ } else {
+ show_error("Momsregler", resp);
+ }
+ free(resp);
+ return 0;
+}
+
+static int rule_form(struct app *a, const struct rule *r)
+{
+ static int sel = 0;
+ char box[16] = "", pattern[128] = "", sort[16] = "10";
+ int mti = 0, signi = 0;
+ if (r) {
+ snprintf(box, sizeof box, "%s", r->box);
+ for (int i = 0; i < RULE_NTYPES; i++)
+ if (strcmp(RULE_TYPES[i], r->match_type) == 0)
+ mti = i;
+ snprintf(pattern, sizeof pattern, "%s", r->pattern);
+ signi = r->sign < 0 ? 1 : 0;
+ snprintf(sort, sizeof sort, "%lld", (long long)r->sort_order);
+ }
+ struct tui_form_field ff[5];
+ memset(ff, 0, sizeof ff);
+ ff[0].label = "Ruta";
+ ff[0].value = box;
+ ff[0].cap = sizeof box;
+ ff[0].kind = TUI_F_TEXT;
+ ff[1].label = "Typ";
+ ff[1].kind = TUI_F_CHOICE;
+ ff[1].choices = RULE_TYPES;
+ ff[1].nchoices = RULE_NTYPES;
+ ff[1].choice = &mti;
+ ff[2].label = "Mönster";
+ ff[2].value = pattern;
+ ff[2].cap = sizeof pattern;
+ ff[2].kind = TUI_F_TEXT;
+ ff[3].label = "Tecken";
+ ff[3].kind = TUI_F_CHOICE;
+ ff[3].choices = RULE_SIGNS;
+ ff[3].nchoices = RULE_NSIGNS;
+ ff[3].choice = &signi;
+ ff[4].label = "Sortering";
+ ff[4].value = sort;
+ ff[4].cap = sizeof sort;
+ ff[4].kind = TUI_F_TEXT;
+ const char *title = r ? "Redigera momsregel" : "Ny momsregel";
+ for (;;) {
+ int rr = tui_form_run(title, ff, 5, 1, &sel);
+ if (rr == TUI_FORM_BACK)
+ return 0;
+ if (rr == TUI_FORM_REFRESH) {
+ rule_save(a, r ? r->id : 0, box, mti, pattern, signi, sort, 1);
+ continue;
+ }
+ if (rr == TUI_FORM_SUBMIT) {
+ if (rule_save(a, r ? r->id : 0, box, mti, pattern, signi, sort, 0))
+ return 1;
+ continue;
+ }
+ }
+}
+
+static void rule_delete(struct app *a, const struct rule *r)
+{
+ if (!tui_confirm("Momsregler", "Ta bort regeln %s %s %s?", r->box,
+ r->match_type, r->pattern))
+ return;
+ char args[64];
+ snprintf(args, sizeof args, "{\"id\":%lld}", (long long)r->id);
+ char *resp =
+ client_rpc(&a->conn, "report.rule_delete", a->session, a->org, args);
+ if (resp && client_ok(resp))
+ tui_message("Momsregler", "Regeln borttagen.");
+ else
+ show_error("Momsregler", resp);
+ free(resp);
+}
+
+void rules_screen(struct app *a)
+{
+ int cursor = 0;
+ for (;;) {
+ if (g_quit)
+ return;
+ char *resp = client_rpc(&a->conn, "report.rule_list", a->session,
+ a->org, "{\"report\":\"vat\"}");
+ if (!resp || !client_ok(resp)) {
+ show_error("Momsregler", resp);
+ free(resp);
+ return;
+ }
+ size_t n = jarr_size(resp, "result.items");
+ struct rule *rules = xcalloc(n ? n : 1, sizeof *rules);
+ char **items = xcalloc(n + 1, sizeof(char *));
+ for (size_t i = 0; i < n; i++) {
+ char path[64], line[512], boxbuf[24], patbuf[96];
+ snprintf(path, sizeof path, "result.items.%zu.id", i);
+ rules[i].id = jint_val(resp, path, 0);
+ snprintf(path, sizeof path, "result.items.%zu.box", i);
+ char *sbox = jstr_dup(resp, path);
+ snprintf(rules[i].box, sizeof rules[i].box, "%s",
+ sbox ? sbox : "");
+ free(sbox);
+ snprintf(path, sizeof path, "result.items.%zu.match_type", i);
+ char *mt = jstr_dup(resp, path);
+ snprintf(rules[i].match_type, sizeof rules[i].match_type, "%s",
+ mt ? mt : "");
+ free(mt);
+ snprintf(path, sizeof path, "result.items.%zu.pattern", i);
+ char *pat = jstr_dup(resp, path);
+ snprintf(rules[i].pattern, sizeof rules[i].pattern, "%s",
+ pat ? pat : "");
+ free(pat);
+ snprintf(path, sizeof path, "result.items.%zu.sign", i);
+ rules[i].sign = (int)jint_val(resp, path, 1);
+ snprintf(path, sizeof path, "result.items.%zu.sort_order", i);
+ rules[i].sort_order = jint_val(resp, path, 0);
+
+ snprintf(boxbuf, sizeof boxbuf, "%s", rules[i].box);
+ tui_pad_field(boxbuf, sizeof boxbuf, 6);
+ snprintf(patbuf, sizeof patbuf, "%s", rules[i].pattern);
+ tui_pad_field(patbuf, sizeof patbuf, 34);
+ snprintf(line, sizeof line, "%s %-8s %s %-2s %6lld", boxbuf,
+ rules[i].match_type, patbuf,
+ rules[i].sign < 0 ? "-" : "+",
+ (long long)rules[i].sort_order);
+ items[i] = xstrdup(line);
+ }
+ items[n] = xstrdup("+ Ny regel (^N)");
+ int s = tui_select_list("Momsregler", items, (int)n + 1, cursor, 1,
+ &cursor, 1, n > 0 ? "ta bort" : NULL, 0);
+ for (size_t i = 0; i <= n; i++)
+ free(items[i]);
+ free(items);
+ free(resp);
+ if (s == -1) {
+ free(rules);
+ return;
+ }
+ if (s == -6) {
+ if (cursor >= 0 && (size_t)cursor < n)
+ rule_delete(a, &rules[cursor]);
+ } else if (s == -4 || (s >= 0 && (size_t)s == n)) {
+ rule_form(a, NULL);
+ } else if (s >= 0 && (size_t)s < n) {
+ rule_form(a, &rules[s]);
+ }
+ free(rules);
+ }
+}