aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md82
1 files changed, 82 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1311a9d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,82 @@
+# accounted-multicase-intgr
+
+Example integration that sends vouchers (bilag) from a [Multicase](https://www.multicase.no)
+"buntfil" export to [Accounted](https://accounted.se) through its v1 REST API.
+
+It reads the `.xls` export, groups the posting lines into vouchers, maps them to
+Accounted journal entries and creates them as drafts, optionally posting them.
+
+## Install
+
+```sh
+go install simenandre.no/accounted-multicase-intgr@latest
+```
+
+## Usage
+
+```sh
+cp config.example.json config.json # then adjust the mapping
+
+# Parse and map every voucher, no network access
+accounted-multicase-intgr check "MC Buntfil 01102026-12102026.xls"
+accounted-multicase-intgr check -v "MC Buntfil 01102026-12102026.xls" # print each entry
+
+export ACCOUNTED_API_KEY=gnubok_sk_... # scope bookkeeping:write
+export ACCOUNTED_COMPANY_ID=<company uuid>
+# export ACCOUNTED_URL=https://app.accounted.se
+
+accounted-multicase-intgr push -dry-run export.xls # server validates, nothing is saved
+accounted-multicase-intgr push export.xls # create drafts
+accounted-multicase-intgr push -commit export.xls # create and post, assigning voucher numbers
+```
+
+`push` validates everything before it writes anything: if any voucher has an
+unmapped account or VAT code, or a date outside an open fiscal period, nothing
+is sent.
+
+## How vouchers are mapped
+
+- **Grouping.** Lines are grouped by `BilagsArt` and `Bilagsnummer`. Inventory
+ corrections (Lagerkorrigering) carry Bilagsnummer 0 and are grouped per
+ batch (`BuntHodeID`) instead.
+- **Amounts.** `Belop` is used, in the Multicase company's base currency.
+ Positive is debit, negative is credit. Amounts are rounded to two decimals;
+ Multicase writes currency conversions with three, so a voucher can end up a
+ few öre off. That difference, up to `max_rounding_ore`, is posted to
+ `rounding_account`. Zero-amount lines are dropped.
+- **Accounts.** A line whose `Konto` equals the customer number (`Faktura
+ kundenr`) or supplier number (`LevNr.`) is a subledger line and is posted to
+ `customer_ledger_account` / `supplier_ledger_account`. Every other account
+ must be listed in `accounts`.
+- **VAT.** Multicase posts revenue and cost lines gross with an MVA code and
+ leaves the split to the receiving ledger. For codes with a `rate`, the net
+ amount stays on the mapped account and the VAT goes to the code's `account`.
+ Codes with rate 0 pass through untouched.
+- **Dimensions.** `AvdNr.` and `Prosjekt` are sent as the SIE dimensions named
+ in `dimensions` (by default 1, kostnadsställe, and 6, projekt).
+
+`config.example.json` maps the accounts found in one Print Supplies AS export
+to BAS 2026 accounts. It is a starting point only: have the mapping, and in
+particular the VAT codes, checked by whoever keeps the books.
+
+## Not booking twice
+
+Each voucher is sent with `source_id` `multicase:<BilagsArt>:<Bilagsnummer>`
+and a deterministic `Idempotency-Key`, so retries after a timeout are replayed
+by the server rather than repeated. Accounted keeps idempotency keys for 24
+hours, so `push` also records every draft and posted voucher in
+`multicase-state.json`. Re-running an overlapping export skips what was sent.
+
+A voucher that changed in Multicase after it was sent is reported and skipped.
+Posted vouchers cannot be edited; correct them in Accounted (storno or rättelse).
+
+## Limitations
+
+- Accounted keeps its ledger in SEK. Amounts are sent as they appear in
+ Multicase, so the export's base currency must match the Accounted company's.
+- Only the first worksheet of the export is read, and only Excel 97-2003
+ (`.xls`) files are supported. The bundled BIFF8 reader exists because the
+ available Go libraries decode negative whole-number cells as large positive
+ numbers.
+- Requests are throttled to stay under the default limit of 100 requests per
+ minute per API key; 429 and 5xx responses are retried.