diff options
Diffstat (limited to 'src/commands.c')
| -rw-r--r-- | src/commands.c | 87 |
1 files changed, 81 insertions, 6 deletions
diff --git a/src/commands.c b/src/commands.c index e16ba75..d7f3860 100644 --- a/src/commands.c +++ b/src/commands.c @@ -3185,14 +3185,15 @@ static yyjson_mut_val *h_voucher_correct(struct req *r) /* ------------------------------------------------------------------ */ static int link_attachment(struct req *r, int64_t aid, int64_t vid, - char **errmsg) + char **errmsg, const char **errcode) { + *errcode = "INTERNAL"; sqlite3_stmt *st = NULL; if (sqlite3_prepare_v2( r->db, "SELECT (SELECT count(*) FROM vouchers WHERE org_id=?1 AND id=?2)," "(SELECT count(*) FROM voucher_attachments WHERE org_id=?1" - " AND attachment_id=?3)," + " AND voucher_id=?2 AND attachment_id=?3)," "(SELECT count(*) FROM attachments WHERE org_id=?1 AND id=?3)", -1, &st, NULL) != SQLITE_OK) { *errmsg = xstrdup("database error"); @@ -3210,14 +3211,17 @@ static int link_attachment(struct req *r, int64_t aid, int64_t vid, sqlite3_finalize(st); if (!aexists) { *errmsg = xstrdup("attachment not found"); + *errcode = "NOT_FOUND"; return -1; } if (!vexists) { *errmsg = xstrdup("voucher not found"); + *errcode = "NOT_FOUND"; return -1; } if (linked) { *errmsg = xstrdup("attachment is already linked"); + *errcode = "CONFLICT"; return -1; } char ts[32]; @@ -3290,8 +3294,10 @@ static yyjson_mut_val *h_attachment_put(struct req *r) free(content); if (voucher_id) { char *emsg = NULL; - if (link_attachment(r, existing, voucher_id, &emsg) != 0) { - yyjson_mut_val *res = fail(r, "CONFLICT", emsg); + const char *ecode = NULL; + if (link_attachment(r, existing, voucher_id, &emsg, &ecode) != 0) { + yyjson_mut_val *res = + fail(r, ecode ? ecode : "INTERNAL", emsg); free(emsg); return res; } @@ -3331,8 +3337,9 @@ static yyjson_mut_val *h_attachment_put(struct req *r) if (voucher_id) { char *emsg = NULL; - if (link_attachment(r, id, voucher_id, &emsg) != 0) { - yyjson_mut_val *res = fail(r, "NOT_FOUND", emsg); + const char *ecode = NULL; + if (link_attachment(r, id, voucher_id, &emsg, &ecode) != 0) { + yyjson_mut_val *res = fail(r, ecode ? ecode : "INTERNAL", emsg); free(emsg); return res; } @@ -3347,6 +3354,70 @@ static yyjson_mut_val *h_attachment_put(struct req *r) return o; } +static yyjson_mut_val *h_attachment_link(struct req *r) +{ + int64_t id = 0, voucher_id = 0; + arg_int(r->args, "id", &id); + arg_int(r->args, "voucher_id", &voucher_id); + if (id <= 0 || voucher_id <= 0) + return fail(r, "INVALID_ARGS", "id and voucher_id are required"); + if (r->dry_run) { + yyjson_mut_val *o = yyjson_mut_obj(r->rdoc); + yyjson_mut_obj_add_bool(r->rdoc, o, "dry_run", true); + return o; + } + char *emsg = NULL; + const char *ecode = NULL; + if (link_attachment(r, id, voucher_id, &emsg, &ecode) != 0) { + yyjson_mut_val *e = fail(r, ecode ? ecode : "INTERNAL", emsg); + free(emsg); + return e; + } + char *reqjson = audit_args_json(r->args); + audit_append(r->db, r->org_id, r->sess->user_id, r->sess->token_id, + "attachment.link", reqjson, "OK", NULL); + free(reqjson); + yyjson_mut_val *o = yyjson_mut_obj(r->rdoc); + yyjson_mut_obj_add_int(r->rdoc, o, "id", id); + yyjson_mut_obj_add_int(r->rdoc, o, "voucher_id", voucher_id); + return o; +} + +static yyjson_mut_val *h_attachment_unlink(struct req *r) +{ + int64_t id = 0, voucher_id = 0; + arg_int(r->args, "id", &id); + arg_int(r->args, "voucher_id", &voucher_id); + if (id <= 0 || voucher_id <= 0) + return fail(r, "INVALID_ARGS", "id and voucher_id are required"); + if (r->dry_run) { + yyjson_mut_val *o = yyjson_mut_obj(r->rdoc); + yyjson_mut_obj_add_bool(r->rdoc, o, "dry_run", true); + return o; + } + sqlite3_stmt *st = NULL; + if (sqlite3_prepare_v2( + r->db, + "DELETE FROM voucher_attachments WHERE org_id=?1" + " AND voucher_id=?2 AND attachment_id=?3", + -1, &st, NULL) != SQLITE_OK) + return fail(r, "INTERNAL", "database error"); + sqlite3_bind_int64(st, 1, r->org_id); + sqlite3_bind_int64(st, 2, voucher_id); + sqlite3_bind_int64(st, 3, id); + int rc = sqlite3_step(st); + sqlite3_finalize(st); + if (rc != SQLITE_DONE) + return fail(r, "INTERNAL", sqlite3_errmsg(r->db)); + if (sqlite3_changes(r->db) == 0) + return fail(r, "NOT_FOUND", "link not found"); + char *reqjson = audit_args_json(r->args); + audit_append(r->db, r->org_id, r->sess->user_id, r->sess->token_id, + "attachment.unlink", reqjson, "OK", NULL); + free(reqjson); + return yyjson_mut_obj(r->rdoc); +} + static yyjson_mut_val *h_attachment_get(struct req *r) { int64_t id = 0; @@ -3844,6 +3915,10 @@ const struct command g_commands[] = { h_template_archive }, { "attachment.put", "Store an underlag (receipt, invoice)", PERM_WRITE, 1, 1, 1, h_attachment_put }, + { "attachment.link", "Link an existing attachment to a voucher", + PERM_WRITE, 1, 1, 1, h_attachment_link }, + { "attachment.unlink", "Remove an attachment link", PERM_WRITE, 1, 1, 1, + h_attachment_unlink }, { "attachment.get", "Fetch an attachment", PERM_READ, 1, 0, 0, h_attachment_get }, { "attachment.list", "List attachments / inbox", PERM_READ, 1, 0, 0, |
