diff options
Diffstat (limited to 'clients/screens_rules.c')
| -rw-r--r-- | clients/screens_rules.c | 226 |
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); + } +} |
