summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-21 23:09:57 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-21 23:09:57 +0200
commit8350815a8c6987b288551848eda1c678739c8d4a (patch)
tree04ef3c56e10fa8b06768465910d10a57779e8731 /tests
parentbf9012dad7ef34bbbfb073bc0f37b0161f0496f3 (diff)
downloadbokf-0.1.62.tar.gz
bokf-0.1.62.zip
invoices: text rows, duplicate and payment link (schema v12); smtp address checksv0.1.62
Diffstat (limited to 'tests')
-rw-r--r--tests/invoice_check.c18
-rw-r--r--tests/test_core.c140
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" },