diff options
Diffstat (limited to 'docs/PROTOCOL.md')
| -rw-r--r-- | docs/PROTOCOL.md | 114 |
1 files changed, 84 insertions, 30 deletions
diff --git a/docs/PROTOCOL.md b/docs/PROTOCOL.md index 76197d9..75ae963 100644 --- a/docs/PROTOCOL.md +++ b/docs/PROTOCOL.md @@ -374,26 +374,36 @@ resolved rows in a dry run. | Command | Args | Notes | |---|---|---| | `settings.get` | — | effective org settings (defaults included); secret values replaced by `_set` flags | -| `settings.set` | `key`, `value?` | known keys: `default_series`, `attachment_dir`, `bank_account`, `invoice_receivable_account`, `invoice_revenue_account`, `invoice_bankgiro`, `invoice_our_ref`, `smtp_host`, `smtp_port`, `smtp_user`, `smtp_from`, `smtp_reply_to`, `smtp_security`, `smtp_password` | +| `settings.set` | `key`, `value?` | known keys: `default_series`, `series_voucher`, `series_invoice`, `series_payroll`, `series_bokslut`, `series_ib`, `attachment_dir`, `bank_account`, `invoice_receivable_account`, `invoice_revenue_account`, `invoice_bankgiro`, `invoice_our_ref`, `document_header_color`, `smtp_host`, `smtp_port`, `smtp_user`, `smtp_from`, `smtp_reply_to`, `smtp_security`, `smtp_password` | -`default_series` (1–8 characters, e.g. `A`, `V-`, `A `) is used when -`voucher.post` carries no `series` and as the default series for new -templates. `attachment_dir` (a path, up to 255 characters) is accepted for +`series_voucher`, `series_invoice`, `series_payroll`, `series_bokslut` and +`series_ib` (1–8 characters, e.g. `A`, `V-`, `Å`) are the number series each +feature posts into: manual vouchers, `invoice.issue`, payroll runs and +payments, `bokslut.post` and ingående balans. They default to `A`, `F`, `L`, +`Å` and `IB`; `series_voucher` also falls back to the legacy +`default_series` (1–8 characters) when unset, and new templates default to +it. Reports and SIE count both the configured `series_ib` and the +historical `IB` series as ingående balans. `attachment_dir` (a path, up to +255 characters) is accepted for compatibility, but the TUI file browser now remembers the directory of the last picked attachment client-side. `bank_account` (digits only, up to 10 characters, default `1930`) is the account `bank.import` uses when the request carries no `account`. `invoice_receivable_account` (default `1510`) and `invoice_revenue_account` (default `3001`) are the receivable and default revenue account of invoice postings, digits only, up to 10 -characters. Verification ids are the concatenation of series and number +characters. `document_header_color` (`#rrggbb`, default `#314c59`) is the +background colour of the generated documents' header bars (invoice and +lönebesked); the invoice prints the org name on it. Verification ids are the +concatenation of series and number (`V-8`), and series are free-form: only an unbroken numbering per series is required. `smtp_host` (up to 255 characters, no control characters), `smtp_user` (up to -255), `smtp_from` and `smtp_reply_to` (up to 254), `smtp_port` (digits, -1–65535) and `smtp_security` (`starttls`, `tls` or `plain`, default -`starttls` when unset) configure the outgoing mail used when invoices are -sent. +255), `smtp_from` and `smtp_reply_to` (up to 254) must be e-mail addresses +(one `@`, no spaces), `smtp_port` (digits, 1–65535) and `smtp_security` +(`starttls`, `tls` or `plain`, default `starttls` when unset) configure the +outgoing mail used when invoices are sent. The sender's display name is the +organization name; `smtp_from` is the address. `smtp_password` is a secret setting. `settings.set` encrypts the value with AES-256-GCM under the key in the `BOKFD_SECRET_KEY` environment variable (32 @@ -441,7 +451,17 @@ linked to that voucher and each item carries that `voucher_id`. | `sru.export` | `fiscal_year`, `adjustments?`, `submitter?`, `assisted?`, `audited?`, `ignore_unmapped?` | `INFO.SRU` + `BLANKETTER.SRU` (ISO-8859-1, base64) | All reports are pure reads, respect locks, and return JSON rows. Amounts are -öre. `report.general_ledger` (huvudbok) returns account blocks: +öre. IB (ingående balans) for a balance account (asset, liability, equity) is +all earlier history, including earlier years' `IB` vouchers, plus this +year's `IB` vouchers; a P&L account (revenue, expense) restarts at zero at +every fiscal-year start, so its IB is only this year's `IB` vouchers and, +with a narrowed `from`, the year's movements before it. +`report.income_statement` (and therefore `sru.export` and the TUI +resultatrapport) ignores the source system's `"Stäng ..."` closing vouchers +in SIE-imported years (`source:"sie_import"`), where the P&L accounts are +closed straight to 2099 and would otherwise net to zero; the TUI +årsredovisning uses the same rule. `report.general_ledger` (huvudbok) +returns account blocks: `{"fiscal_year","from","to","last_voucher":{...},"accounts":[{"account", "name","ib_ore","debit_ore","credit_ore","ub_ore","rows":[{"series", "number","date","description","row_description","debit_ore","credit_ore", @@ -472,7 +492,10 @@ like the blankett; box 48 positive as filed) and returns `{"org_nr", bytes are ISO-8859-1, so write them verbatim to a `.xml` file. Both cover the whole fiscal year; the ledger's period can be narrowed with `from`/`to`. `report.vat` returns `{"from","to","boxes":[{"box":"05","label":"...","amount_ore":...}],"note"}`. -Rules sharing a box are summed into a single entry. `box 49` is the sum of +Rules sharing a box are summed into a single entry. The momsomföring +itself (any voucher with a row on 2650) and SIE-imported `"Stäng ..."` year +closings are left out, so a period that includes its own VAT settlement +still reports the underlying boxes. `box 49` is the sum of the moms boxes (`10`,`11`,`12`,`30`,`31`,`32`,`48`,`60`,`61`,`62`), so box 48 is signed like the blankett (ingående moms negative); underlag boxes do not change what is payable. @@ -509,7 +532,8 @@ support `dry_run`, which validates without writing. | `sie.import` | `content_base64` or `path`, `dry_run?` | one file per call; creates missing accounts and posts #VER as `source:"sie_import"`; only into an empty org fiscal year; `#IB` becomes an `IB` voucher when the year has no earlier history, otherwise the earlier vouchers carry the balances | SIE 4 files are written in CP437 with PC8 format, `#SIETYP 4`, `#FNR`, `#ORGNR`, -`#KONTO`, `#IB`, `#UB`, `#RES`, `#VER`, `#TRANS`. Import is the migration path +`#KONTO`, `#IB`, `#UB`, `#RES`, `#VER`, `#TRANS`. `#IB`/`#UB` are written for +balance accounts and `#RES` for P&L accounts, with IB as in §7.6. Import is the migration path from Fortnox/Visma/BL and must be dry-run first; it reports exactly what would be created. @@ -585,10 +609,11 @@ removes one link and is a `NOT_FOUND` when it does not exist. Both mutate | `invoice.sequence_set` | `next_number` (owner) | `next_number` | | `invoice.preview` | draft (below) | `content_base64`, `number`, `ocr`, `net_ore`, `vat_ore`, `total_ore` | | `invoice.issue` | draft, `dry_run?` | `id`, `number`, `ocr`, `document_id`, `voucher_id`, totals | -| `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.get` | `id` | header, `rows[]`, `document_id`, `voucher_id`, `paid_date`, `payment_voucher_id`, `last_sent_at`, `last_sent_to` | +| `invoice.list` | `customer_id?`, `status?` (`issued`/`credited`), `limit?` | `items[]`, newest first, with `paid_date` | | `invoice.pdf` | `id` | stored PDF as `content_base64` | | `invoice.send` | `id`, `to?` | `id`, `sent_to`, `at`; `dry_run` returns `to`, `subject` | +| `invoice.pay` | `id`, `voucher_id`, `dry_run?` | `id`, `number`, `paid_date`, `payment_voucher_id`, `voucher_series`, `voucher_number` | The draft object is the argument set shared by `invoice.preview` and `invoice.issue`: @@ -611,6 +636,13 @@ accounts are `ACCOUNT_NOT_FOUND`/`ACCOUNT_INACTIVE`. The customer must exist and be active (`NOT_FOUND`). A draft whose rows do not fit the single page is rejected with `TOO_LARGE`. +A row with `"text": true` is a free-text line: only `description` is used, +it has no quantity, unit, price or VAT, contributes nothing to the totals +and only prints its description in the document. Every draft still needs at +least one priced row (`INVALID_ARGS` otherwise, "invoice total must be +greater than zero"). `invoice.get` and `invoice.list` return rows with +`is_text`. + Numbering is a per-org, global series: `invoice_sequence.next_number` starts at 1, is set by the owner and is incremented by exactly one per issued invoice. The OCR reference is the number followed by its MOD10 (Luhn) check @@ -629,7 +661,9 @@ is the idempotency key). `invoice.sequence_set` and `invoice.issue` are audited; `invoice.issue` supports `dry_run`, which validates and renders but takes no number and writes nothing. `invoice.pdf` returns the stored document as base64 -(`JVBERi0` after decoding is the PDF magic). When the setting +(`JVBERi0` after decoding is the PDF magic). The document's header bar +prints the organization `name` and is coloured by `document_header_color` +(default `#314c59`; invalid stored values fall back to it). When the setting `invoice_bankgiro` is present it is printed in the document's Bankgiro field; `invoice_our_ref` (up to 64 characters) prefills the invoice form's "Vår referens". @@ -647,6 +681,15 @@ 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. +`invoice.pay` links a payment voucher (created by the client, normally from +the TUI's **Kvittera betalning** action, which prefills debit `bank_account` +and credit `invoice_receivable_account` in the ordinary voucher form) and +stamps `paid_date` with the voucher's date. The voucher must credit the +`invoice_receivable_account` (default `1510`) with exactly the invoice +total, else `INVALID_ARGS`; an already paid invoice and a `credited` one are +rejected (`CONFLICT` and `INVALID_ARGS`). `dry_run` validates without +writing, and the command is audited. + ### 7.11 Anställda (employees) The employee register. `personal_no` is checked for shape (10 or 12 digits, @@ -732,7 +775,8 @@ given date (default: today) and sets the run's `status` to `paid` with `payment_voucher_id`; paying twice is a `CONFLICT`. `payroll.payslip` renders one A4 lönebesked for an employee line of a posted -run: the employer header and footer, the employee name, the masked +run: the employer name in the header bar (coloured by +`document_header_color`), the employer footer, the employee name, the masked personnummer (`********-1234`, or `********` when the key is unavailable), period, pay date, tax table and column, then Bruttolön, the negative Preliminärskatt, a rule and Nettolön, and the note `Arbetsgivaravgifter @@ -857,6 +901,7 @@ Args: `name:type(values)[!][=default]`, `!` = required. | `invoice.list` | viewer | yes | no | no | `customer_id:int`, `status:enum(issued\|credited)`, `limit:int=200` | | `invoice.pdf` | viewer | yes | no | no | `id:int!` | | `invoice.send` | bookkeeper | yes | yes | yes | `id:int!`, `to:string` | +| `invoice.pay` | bookkeeper | yes | yes | yes | `id:int!`, `voucher_id:int!` | | `employee.list` | viewer | yes | no | no | `active_only:bool` | | `employee.get` | viewer | yes | no | no | `id:int!` | | `employee.create` | bookkeeper | yes | yes | yes | `name:string!`, `personal_no:string!`, `address:string`, `postal_code:string`, `city:string`, `bank_account:string`, `email:string`, `salary_account:string`, `monthly_salary_ore:int=0`, `tax_table:int=30`, `tax_column:int=1` | @@ -902,7 +947,10 @@ commands. Implemented screens (0.1.0-dev): - **Mallar** — list, create and edit templates in the same form style as vouchers (Tab, dynamic rows, F7 clear row, F5 validate, F9 save); archive keeps the template but hides it from the list. -- **Underlag** — inbox of unlinked attachments; `a` uploads a file. +- **Underlag** — inbox of unlinked attachments; `a` uploads a file and + Enter opens **Granska** (text in a pager, other files in the desktop + viewer) or **Ladda ned…**; the voucher detail's underlag list (`f`) works + the same way. - **Bankavstämning** — imported bank transactions (`bank.import`) matched against vouchers on the bank account, with suggestions; Enter matches the suggested voucher (or picks another), `u` unmatches, `a` imports a SEB CSV. @@ -912,16 +960,21 @@ commands. Implemented screens (0.1.0-dev): returns to the list. A failed auto-match keeps the posted voucher and shows the server error. - **Fakturor** — invoice list (`invoice.list`, newest first) with number, - date, customer, total and status (`utfärdad`/`krediterad`). Ctrl+N opens - the form, Enter the detail. The form has the customer picker, invoice/due - (due defaults from the customer's payment days) and delivery dates, er/var - referens and rows (beskrivning, antal, enhet, à-pris, moms, anm); `F5` - previews the real PDF (`invoice.preview`, nothing stored, no number - consumed), `Ctrl+Enter` issues (`invoice.issue`) and then asks - "Skicka faktura <nr> till <e-post>?". The detail shows header and rows; - `p` fetches the stored PDF (`invoice.pdf`) and `s` sends it - (`invoice.send`). In the list, `n` sets the next invoice number - (`invoice.sequence_get`/`sequence_set`, owner-only). + date, customer, total and status (`utfärdad`/`krediterad`/`betald + <datum>`). Ctrl+N opens the form, Enter the detail. The form has the + customer picker, invoice/due (due defaults from the customer's payment + days) and delivery dates, er/var referens and rows (beskrivning, antal, + enhet, à-pris, moms, anm); a row with only beskrivning is a free-text line + (`text` rows, no amount). `F5` previews the real PDF (`invoice.preview`, + nothing stored, no number consumed), `F9` issues (`invoice.issue`) and + then asks "Skicka faktura <nr> till <e-post>?". The detail shows header + and rows and offers `p = visa PDF` (`invoice.pdf`), `s = skicka` + (`invoice.send`), `u = duplicera` (a new draft with the same rows and + today's dates) and, on unpaid invoices, `b = kvittera betalning`: a + prefilled payment voucher (debit `bank_account`, credit + `invoice_receivable_account`) is opened in the ordinary voucher form and, + once posted, linked with `invoice.pay`. In the list, `n` sets the next + invoice number (`invoice.sequence_get`/`sequence_set`, owner-only). - **Kunder** — the customer register (name, address, postal code, city, VAT number, e-mail, your reference, payment days, notes). Ctrl+N creates, Enter edits (F5 validates with a dry run, Ctrl+Enter saves), `d` @@ -963,10 +1016,11 @@ commands. Implemented screens (0.1.0-dev): - **Bolaget** — the dashboard's hub for the org's master data: **Företagsuppgifter** (name, org number, VAT number, address, e-mail, phone, moms period, framework, fiscal-year start month), editable in - place by owners, others see it read-only; **Fakturauppgifter** - (standardserie, fordringskonto, intäktskonto, bankgiro, vår referens) and + place by owners, others see it read-only; **Fakturauppgifter** (nästa + fakturanummer (owner), fordringskonto, intäktskonto, bankgiro, vår + referens); **Verifikationsserier** (the per-feature series); and **E-post (SMTP)** (host, port, user, sender, reply-to, security, - password), both `settings.set` and open to bookkeepers; + password), the last three `settings.set` and open to bookkeepers; **Styrelseledamöter**; and the registers **Anställda**, **Kunder** and **Momsregler**. - **System** — the hub with **Skattetabeller** and **Revision**. The file |
