aboutsummaryrefslogtreecommitdiff
path: root/docs/PROTOCOL.md
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-20 15:57:08 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-20 15:57:08 +0200
commit90068419d51b0289c65d4db9e14849fbecd34a95 (patch)
tree8d9dbf65ee6d869a6d3cac94ec0dc7639f9d6f8a /docs/PROTOCOL.md
parent9c3892b09a439e5bafa80b7c9ef910b5af0d8598 (diff)
downloadbokf-90068419d51b0289c65d4db9e14849fbecd34a95.tar.gz
bokf-90068419d51b0289c65d4db9e14849fbecd34a95.zip
invoice: send the issued PDF over SMTP
Diffstat (limited to 'docs/PROTOCOL.md')
-rw-r--r--docs/PROTOCOL.md17
1 files changed, 16 insertions, 1 deletions
diff --git a/docs/PROTOCOL.md b/docs/PROTOCOL.md
index a4e61ec..95c2369 100644
--- a/docs/PROTOCOL.md
+++ b/docs/PROTOCOL.md
@@ -156,7 +156,8 @@ enforced at the database level via composite keys (see `SCHEMA.md`).
`ORG_REQUIRED`, `ORG_FORBIDDEN`, `FORBIDDEN`, `NOT_FOUND`, `CONFLICT`,
`UNBALANCED`, `ACCOUNT_NOT_FOUND`, `ACCOUNT_INACTIVE`, `FISCAL_YEAR_NOT_FOUND`,
`FISCAL_YEAR_CLOSED`, `PERIOD_LOCKED`, `DATE_OUT_OF_RANGE`, `IMMUTABLE`,
-`SEQUENCE_GAP`, `TOO_LARGE`, `UNSUPPORTED`, `DB_BUSY`, `INTERNAL`.
+`SEQUENCE_GAP`, `TOO_LARGE`, `UNSUPPORTED`, `SMTP_NOT_CONFIGURED`,
+`SMTP_FAILED`, `DB_BUSY`, `INTERNAL`.
Codes are stable; `message` is human-readable and may change. `details` is
machine-readable where offered.
@@ -584,6 +585,7 @@ removes one link and is a `NOT_FOUND` when it does not exist. Both mutate
| `invoice.get` | `id` | header, `rows[]`, `document_id`, `voucher_id`, `last_sent_at`, `last_sent_to` |
| `invoice.list` | `customer_id?`, `status?` (`issued`/`credited`), `limit?` | `items[]`, newest first |
| `invoice.pdf` | `id` | stored PDF as `content_base64` |
+| `invoice.send` | `id`, `to?` | `id`, `sent_to`, `at`; `dry_run` returns `to`, `subject` |
The draft object is the argument set shared by `invoice.preview` and
`invoice.issue`:
@@ -627,6 +629,19 @@ writes nothing. `invoice.pdf` returns the stored document as base64
(`JVBERi0` after decoding is the PDF magic). When the setting
`invoice_bankgiro` is present it is printed in the document's Bankgiro field.
+`invoice.send` mails the stored PDF to the customer's `email` (or the `to`
+override) with subject `Faktura <number>` and a Swedish body. It needs the
+settings `smtp_host` and `smtp_from`; `smtp_port` defaults to 587 and
+`smtp_security` to `starttls`. When `smtp_user` is set, the secret
+`smtp_password` must be present and decryptable with the daemon's key, else
+the command is `SMTP_NOT_CONFIGURED`. The password is decrypted from the
+encrypted setting, handed to the SMTP client and never written to the audit
+log or returned in an error. A refused or failed delivery is `SMTP_FAILED`
+with the client's error text. On success `last_sent_at`/`last_sent_to` are
+updated and the `invoice.send` audit entry stores `{id,to,subject}` only.
+`dry_run` validates configuration, recipient and stored document and returns
+the recipient and subject without sending or updating anything.
+
## 8. The TUI is just a client
`bokftui` logs in over the same socket, picks an org and issues the same