Prototypes Prototype docsP11 · Docs and assistant
Bookkeeping (P32)
DocsPage 32 of 39

Bookkeeping (P32)

Folio keeps a small Danish business's books on each laptop, works with no network, and tests after every change that the books balance.

The business is Kanal Studio ApS, a design studio in Copenhagen. Anna and Ben each keep the books on their own laptop and sync through the prototypes' server. The books start with a seeded quarter: July to September 2026, with invoices, bills, rent, salaries and the second quarter's VAT paid.

Entries

An entry has a date, a text, attachments by name, and lines. Each line has an account from a Danish standard chart of accounts, a debit or a credit, and a VAT code. Every amount is a whole number of øre.

An entry that does not balance cannot be saved. The editor shows the debit, the credit and the difference while you type. Save then shows how much the two sides differ.

A line with the code S25 gets an output VAT line ("salgsmoms") of 25 % on account 6900. A line with K25 gets an input VAT line ("købsmoms") on account 6910. A service bought abroad has the code RC25. It gets 2 VAT lines that cancel each other: input VAT, and VAT on services bought abroad on account 6920. This is the Danish reverse charge.

Invoices, bills and the bank

An invoice posts the receivable, the sale and the output VAT. The server gives it its number when it puts the events in order, so 2 laptops never use the same number. A bill posts the cost, the input VAT and the payable.

The Bank page reads the CSV file of a Danish bank: semicolons, dates such as 24.09.2026, amounts such as 80.000,00, in Windows-1252 or UTF-8. The prototype has 2 sample files from Danske Bank that overlap by 3 days. A line's id is made from its date, amount, text and balance, so the overlapping lines are imported once.

Folio suggests the open invoice, bill, salary or VAT settlement each bank line pays. The score adds points for the amount, for a number or a name in the text, and for a date near the due date. The page shows the confidence and the reasons. When 2 invoices are open for the same amount, the reasons show which one the text names. A person confirms the match or picks another, and the match posts the payment. A line with no match, such as a bank charge, is posted to an account.

Reports and the VAT return

The trial balance, the profit and loss and the balance sheet are worked out from the entries for a quarter or the year. Each figure opens the lines behind it.

The VAT return ("momsangivelse") has the boxes of the Danish form:

  • output VAT,
  • VAT on goods bought abroad,
  • VAT on services bought abroad,
  • input VAT,
  • boxes A, B and C,
  • the amount to pay or to get back.

Each box is worked out from the VAT lines. It is checked against 25 % of the lines with a VAT code, and against the VAT accounts. Settling the VAT moves the quarter's VAT to the settlement account (6950) and locks the quarter. After that, no laptop can post, correct or reverse an entry dated in it.

Two laptops, offline

The books are a log of events that is only ever added to. A laptop keeps the events in IndexedDB, and a service worker keeps the app, so the app opens and posts with no network. An event's id is the laptop and its sequence number, such as Anna:12. An event sent again after a lost answer is therefore stored once.

A correction posts 2 entries in one event: the entry that reverses the old one, and the corrected entry. The old entry stays as it was, so 2 laptops cannot overwrite each other. When both laptops post offline, both entries arrive and the trial balance still sums to zero. When both match the same bank line offline, the server takes the first to arrive. The other laptop then shows who matched the line first.

The proof

A property test runs 2 laptops and the server from a seed, with its own random generator. Each step is one random operation, such as an entry, an invoice, a bank file, a match or a closed quarter. Other steps try an entry that does not balance, take a laptop off the network, or sync. Some syncs are lost on the way.

After every step the test checks the server and both laptops for 6 things:

  • every entry balances,
  • the trial balance sums to zero,
  • the VAT return equals the VAT postings,
  • a locked quarter does not change,
  • nothing is counted twice,
  • every change is kept, or refused with a reason.

At the end both laptops must hold the server's entries exactly.

In node the test runs 300 seeds of 60 operations, 18,000 operations in all, and every check passes (node --test prototypes/source/p32/p32.test.mjs). The Proof page runs the same code in the browser.

4 flawed versions show that the test finds each kind of mistake. One server stores a resent event twice, one takes entries in a locked quarter, and one matches a bank line twice. In the fourth, each laptop puts its own events after the others'.

The test found one mistake in the checks while the prototype was built. A laptop's own close of a quarter is not final until the server confirms it. The server may put the other laptop's entries for the quarter before the close. The lock is now checked on the events the server confirmed.

What is stored on the server

The server stores the log of events in its order, the refused events with the reason, and each laptop's last sync. It checks each new event with the same code the laptops run. The log has one order from its first event, so the books are emptied whole 7 days after their last event.

Previous: Email answered by SI (P31). Next: Spreadsheet engine (P33).

How Prototype docs is built

The pages are written in Markdown and built into these pages and an index of 221 sections. The assistant answers from the index, and every answer names the sections it was taken from.