From e9fc91dbc8314a25a0d71b7ee4344d3fc4ccfd5e Mon Sep 17 00:00:00 2001 From: Anders Betts Date: Fri, 18 Sep 2026 22:16:32 +0200 Subject: attachments: link/unlink from the TUI, multi-voucher links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The voucher detail can attach a file to an existing voucher (^F) and the underlag picker (f) can remove a link (d, confirmed). The Underlag inbox links a highlighted item to a voucher picked from a list (k). attachment.link and attachment.unlink are new write commands (dry-run, audited); unlinked files return to the inbox. The same content may now link to several vouchers — only the same voucher/attachment pair is a CONFLICT, matching the table's primary key. --- tests/test_core.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'tests') diff --git a/tests/test_core.c b/tests/test_core.c index aa2bd4a..6f114b9 100644 --- a/tests/test_core.c +++ b/tests/test_core.c @@ -928,6 +928,74 @@ int main(void) CHECK(yyjson_arr_size(jget(d, "result.items")) == 0); yyjson_doc_free(d); + /* the same content may link to several vouchers, but not twice to one */ + d = call(reqf("{\"v\":1,\"id\":\"61a\",\"cmd\":\"attachment.put\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"filename\":" + "\"kvitto.txt\",\"mime\":\"text/plain\"," + "\"content_base64\":\"aGVsbG8gd29ybGQ=\",\"voucher_id\":%lld}}", + g_session, (int)org_id, (long long)sale_id)); + CHECK_OK(d); + CHECK(jint(d, "result.id") == att_id); + yyjson_doc_free(d); + + d = call(reqf("{\"v\":1,\"id\":\"61b\",\"cmd\":\"attachment.put\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"filename\":" + "\"kvitto.txt\",\"mime\":\"text/plain\"," + "\"content_base64\":\"aGVsbG8gd29ybGQ=\",\"voucher_id\":%lld}}", + g_session, (int)org_id, (long long)sale_id)); + CHECK_STR(d, "error.code", "CONFLICT"); + yyjson_doc_free(d); + + /* link/unlink an existing attachment */ + d = call(reqf("{\"v\":1,\"id\":\"61c\",\"cmd\":\"attachment.put\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"filename\":" + "\"extra.txt\",\"content_base64\":\"ZXh0cmE=\"}}", + g_session, (int)org_id)); + CHECK_OK(d); + int64_t orphan = jint(d, "result.id"); + CHECK(orphan > 0); + yyjson_doc_free(d); + + d = call(reqf("{\"v\":1,\"id\":\"61d\",\"cmd\":\"attachment.link\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld," + "\"voucher_id\":%lld}}", + g_session, (int)org_id, (long long)orphan, + (long long)sale_id)); + CHECK_OK(d); + CHECK(jint(d, "result.voucher_id") == sale_id); + yyjson_doc_free(d); + + d = call(reqf("{\"v\":1,\"id\":\"61e\",\"cmd\":\"attachment.link\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld," + "\"voucher_id\":%lld}}", + g_session, (int)org_id, (long long)orphan, + (long long)sale_id)); + CHECK_STR(d, "error.code", "CONFLICT"); + yyjson_doc_free(d); + + d = call(reqf("{\"v\":1,\"id\":\"61f\",\"cmd\":\"attachment.unlink\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld," + "\"voucher_id\":%lld}}", + g_session, (int)org_id, (long long)orphan, + (long long)sale_id)); + CHECK_OK(d); + yyjson_doc_free(d); + + d = call(reqf("{\"v\":1,\"id\":\"61g\",\"cmd\":\"attachment.unlink\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld," + "\"voucher_id\":%lld}}", + g_session, (int)org_id, (long long)orphan, + (long long)sale_id)); + CHECK_STR(d, "error.code", "NOT_FOUND"); + yyjson_doc_free(d); + + d = call(reqf("{\"v\":1,\"id\":\"61h\",\"cmd\":\"attachment.link\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld," + "\"voucher_id\":999999}}", + g_session, (int)org_id, (long long)orphan)); + CHECK_STR(d, "error.code", "NOT_FOUND"); + yyjson_doc_free(d); + /* correction */ d = call(reqf("{\"v\":1,\"id\":\"62\",\"cmd\":\"voucher.correct\"," "\"session\":\"%s\",\"org\":%d,\"args\":{\"voucher\":%lld," -- cgit v1.3