diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/invoice_check.c | 18 | ||||
| -rw-r--r-- | tests/test_core.c | 140 |
2 files changed, 147 insertions, 11 deletions
diff --git a/tests/invoice_check.c b/tests/invoice_check.c index 46b1621..01b5ca3 100644 --- a/tests/invoice_check.c +++ b/tests/invoice_check.c @@ -29,8 +29,8 @@ static void eq64(const char *what, int64_t got, int64_t want) static const struct invoice_line synthetic_lines[] = { { "", "Utvecklingsarbete\nPeriod 2026-01-01 tom 2026-01-31", 61000, "tim", - 120000, 7320000, "", "25" }, - { "A-1", "Konsult", 12500, "tim", 80000, 1000000, "Omvänd moms", "rc" }, + 120000, 7320000, "", "25", 0 }, + { "A-1", "Konsult", 12500, "tim", 80000, 1000000, "Omvänd moms", "rc", 0 }, }; static void make_doc(struct invoice_doc *d) @@ -65,12 +65,12 @@ static void make_doc(struct invoice_doc *d) static void test_totals(void) { static const struct invoice_line lines[] = { - { "", "a", 1000, "st", 1000, 100000, "", "25" }, - { "", "b", 1000, "st", 1000, 50000, "", "12" }, - { "", "c", 1000, "st", 1000, 10000, "", "6" }, - { "", "d", 1000, "st", 1000, 25000, "", "rc" }, - { "", "e", 1000, "st", 1000, 15000, "", "0" }, - { "", "f", 1000, "st", 1000, 4000, "", "eu" }, + { "", "a", 1000, "st", 1000, 100000, "", "25", 0 }, + { "", "b", 1000, "st", 1000, 50000, "", "12", 0 }, + { "", "c", 1000, "st", 1000, 10000, "", "6", 0 }, + { "", "d", 1000, "st", 1000, 25000, "", "rc", 0 }, + { "", "e", 1000, "st", 1000, 15000, "", "0", 0 }, + { "", "f", 1000, "st", 1000, 4000, "", "eu", 0 }, }; struct invoice_doc d; struct invoice_totals t; @@ -89,7 +89,7 @@ static void test_totals(void) { static const struct invoice_line half[] = { - { "", "half", 1000, "st", 100, 50, "", "25" }, + { "", "half", 1000, "st", 100, 50, "", "25", 0 }, }; d.lines = half; diff --git a/tests/test_core.c b/tests/test_core.c index a0b2468..e56c5d3 100644 --- a/tests/test_core.c +++ b/tests/test_core.c @@ -2315,6 +2315,27 @@ static void test_settings(struct tctx *t) CHECK_OK(d); CHECK_STR(d, "result.attachment_dir", "/tmp/bilagor"); yyjson_doc_free(d); + + /* smtp addresses are checked when they are saved */ + d = call(reqf("{\"v\":1,\"id\":\"smtpaddr1\",\"cmd\":\"settings.set\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"key\":" + "\"smtp_from\",\"value\":\"Anders Bergsten\"}}", + g_session, (int)t->org_id)); + CHECK_STR(d, "error.code", "INVALID_ARGS"); + yyjson_doc_free(d); + d = call(reqf("{\"v\":1,\"id\":\"smtpaddr2\",\"cmd\":\"settings.set\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"key\":" + "\"smtp_reply_to\",\"value\":\"inte en adress\"}}", + g_session, (int)t->org_id)); + CHECK_STR(d, "error.code", "INVALID_ARGS"); + yyjson_doc_free(d); + d = call(reqf("{\"v\":1,\"id\":\"smtpaddr3\",\"cmd\":\"settings.set\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"key\":" + "\"smtp_from\",\"value\":\"anders@example.com\"}}", + g_session, (int)t->org_id)); + CHECK_OK(d); + CHECK_STR(d, "result.value", "anders@example.com"); + yyjson_doc_free(d); } static void test_series(struct tctx *t) @@ -2511,7 +2532,7 @@ static void test_smtp_settings(struct tctx *t) d = call(reqf("{\"v\":1,\"id\":\"110\",\"cmd\":\"settings.set\"," "\"session\":\"%s\",\"org\":%d,\"args\":" - "{\"key\":\"smtp_from\",\"value\":\"Bokf AB <a@b.se>\"}}", + "{\"key\":\"smtp_from\",\"value\":\"faktura@example.se\"}}", g_session, (int)t->org_id)); CHECK_OK(d); yyjson_doc_free(d); @@ -2538,7 +2559,7 @@ static void test_smtp_settings(struct tctx *t) CHECK_STR(d, "result.smtp_host", "smtp.example.se"); CHECK_STR(d, "result.smtp_port", "587"); CHECK_STR(d, "result.smtp_user", "faktura@example.se"); - CHECK_STR(d, "result.smtp_from", "Bokf AB <a@b.se>"); + CHECK_STR(d, "result.smtp_from", "faktura@example.se"); CHECK_STR(d, "result.smtp_reply_to", "svar@example.se"); CHECK_STR(d, "result.smtp_security", "tls"); yyjson_doc_free(d); @@ -3405,6 +3426,120 @@ static void test_invoices(struct tctx *t) free(preview_b64); } +static void test_invoice_extras(struct tctx *t) +{ + yyjson_doc *d; + + d = call(reqf("{\"v\":1,\"id\":\"ex1\",\"cmd\":\"customer.create\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"name\":" + "\"Extras AB\"}}", + g_session, (int)t->org_id)); + CHECK_OK(d); + int64_t cust = jint(d, "result.id"); + CHECK(cust > 0); + yyjson_doc_free(d); + + /* a text row contributes nothing and is flagged in the response */ + d = call(reqf("{\"v\":1,\"id\":\"ex2\",\"cmd\":\"invoice.issue\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"customer_id\":" + "%lld,\"invoice_date\":\"2026-09-21\",\"due_date\":" + "\"2026-10-21\",\"rows\":[{\"description\":\"Rubrik\"," + "\"text\":true},{\"description\":\"Konsult\"," + "\"quantity\":\"1\",\"unit\":\"st\",\"unit_price_ore\":" + "100000,\"vat_code\":\"25\"}]}}", + g_session, (int)t->org_id, (long long)cust)); + CHECK_OK(d); + int64_t inv = jint(d, "result.id"); + CHECK(jint(d, "result.total_ore") == 125000); + yyjson_doc_free(d); + + d = call(reqf("{\"v\":1,\"id\":\"ex3\",\"cmd\":\"invoice.get\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld}}", + g_session, (int)t->org_id, (long long)inv)); + CHECK_OK(d); + CHECK(jbool(d, "result.rows.0.is_text")); + CHECK_STR(d, "result.rows.0.description", "Rubrik"); + CHECK(jint(d, "result.rows.0.amount_ore") == 0); + CHECK(!jbool(d, "result.rows.1.is_text")); + CHECK(jint(d, "result.rows.1.amount_ore") == 100000); + CHECK_STR(d, "result.paid_date", ""); + yyjson_doc_free(d); + + /* an all-text draft has no total */ + d = call(reqf("{\"v\":1,\"id\":\"ex4\",\"cmd\":\"invoice.issue\"," + "\"session\":\"%s\",\"org\":%d,\"dry_run\":true,\"args\":" + "{\"customer_id\":%lld,\"invoice_date\":\"2026-09-21\"," + "\"due_date\":\"2026-10-21\",\"rows\":[{\"description\":" + "\"Bara text\",\"text\":true}]}}", + g_session, (int)t->org_id, (long long)cust)); + CHECK_STR(d, "error.code", "INVALID_ARGS"); + yyjson_doc_free(d); + + /* the payment voucher must credit the receivable with the total */ + d = call(reqf("{\"v\":1,\"id\":\"ex5\",\"cmd\":\"voucher.post\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"date\":" + "\"2026-09-21\",\"description\":\"Fel belopp\",\"rows\":" + "[{\"account\":\"1930\",\"debit_ore\":100},{\"account\":" + "\"1510\",\"credit_ore\":100}]}}", + g_session, (int)t->org_id)); + CHECK_OK(d); + int64_t badvoucher = jint(d, "result.id"); + CHECK(badvoucher > 0); + yyjson_doc_free(d); + d = call(reqf("{\"v\":1,\"id\":\"ex6\",\"cmd\":\"invoice.pay\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld," + "\"voucher_id\":%lld}}", + g_session, (int)t->org_id, (long long)inv, + (long long)badvoucher)); + CHECK_STR(d, "error.code", "INVALID_ARGS"); + yyjson_doc_free(d); + + /* the right amount passes, also as a dry run first */ + d = call(reqf("{\"v\":1,\"id\":\"ex7\",\"cmd\":\"voucher.post\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"date\":" + "\"2026-09-22\",\"description\":\"Betalning\",\"rows\":" + "[{\"account\":\"1930\",\"debit_ore\":125000}," + "{\"account\":\"1510\",\"credit_ore\":125000}]}}", + g_session, (int)t->org_id)); + CHECK_OK(d); + int64_t voucher = jint(d, "result.id"); + CHECK(voucher > 0); + yyjson_doc_free(d); + d = call(reqf("{\"v\":1,\"id\":\"ex8\",\"cmd\":\"invoice.pay\"," + "\"session\":\"%s\",\"org\":%d,\"dry_run\":true,\"args\":" + "{\"id\":%lld,\"voucher_id\":%lld}}", + g_session, (int)t->org_id, (long long)inv, + (long long)voucher)); + CHECK_OK(d); + CHECK(jbool(d, "result.dry_run")); + CHECK_STR(d, "result.paid_date", "2026-09-22"); + yyjson_doc_free(d); + d = call(reqf("{\"v\":1,\"id\":\"ex9\",\"cmd\":\"invoice.pay\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld," + "\"voucher_id\":%lld}}", + g_session, (int)t->org_id, (long long)inv, + (long long)voucher)); + CHECK_OK(d); + CHECK_STR(d, "result.paid_date", "2026-09-22"); + const char *ser = jstr(d, "result.voucher_series"); + CHECK(ser && *ser); + yyjson_doc_free(d); + d = call(reqf("{\"v\":1,\"id\":\"ex10\",\"cmd\":\"invoice.get\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld}}", + g_session, (int)t->org_id, (long long)inv)); + CHECK_OK(d); + CHECK_STR(d, "result.paid_date", "2026-09-22"); + CHECK(jint(d, "result.payment_voucher_id") == voucher); + yyjson_doc_free(d); + d = call(reqf("{\"v\":1,\"id\":\"ex11\",\"cmd\":\"invoice.pay\"," + "\"session\":\"%s\",\"org\":%d,\"args\":{\"id\":%lld," + "\"voucher_id\":%lld}}", + g_session, (int)t->org_id, (long long)inv, + (long long)voucher)); + CHECK_STR(d, "error.code", "CONFLICT"); + yyjson_doc_free(d); +} + static void test_invoice_send(struct tctx *t) { yyjson_doc *d; @@ -5267,6 +5402,7 @@ static const struct ttest TESTS[] = { { "smtp_settings", test_smtp_settings, "org_members" }, { "bank", test_bank, "settings" }, { "invoices", test_invoices, "org_members" }, + { "invoice_extras", test_invoice_extras, "invoices" }, { "invoice_send", test_invoice_send, "invoices" }, { "moms_rules", test_moms_rules, "org_members" }, { "rules_editor", test_rules_editor, "moms_rules" }, |
