Prototypes
Folio
P32 · Bookkeeping
Side by side
Anna’s laptop
Ben’s laptop
The proof
The server
How it is built
Kanal Studio ApS
This laptop
Entries
Invoices and bills
Bank
Reports
VAT return
CVR 41 52 63 74, Copenhagen
Entries
Starting
Network
No network.
Every change is kept on this laptop and sent when the network is back.
Entry
1010 Sales of services
1020 Sales of services to the EU
1030 Sales outside the EU
1310 Freelancers and subcontractors
2210 Salaries
2220 Pension
2750 Rent
3610 Software and subscriptions
3620 Office supplies
3640 Phone and internet
4210 Bank charges
5600 Receivables
5800 Bank
6100 Share capital
6110 Retained earnings
6800 Payables
6820 A-tax and AM contribution
6830 Pension payable
6840 Salaries payable
1310 Freelancers and subcontractors
2210 Salaries
2220 Pension
2750 Rent
3610 Software and subscriptions
3620 Office supplies
3640 Phone and internet
4210 Bank charges
No VAT
S25, Output VAT 25 %
K25, Input VAT 25 %
RC25, Service bought abroad, reverse charge 25 %
EU0, Service sold to an EU business, no VAT
X0, Sold outside the EU, no VAT
Hygge Kaffe ApS, Copenhagen
Nordhavn Arkitekter A/S, Copenhagen
Lys & Lyd ApS, Aarhus
Aarhus Bryghus ApS, Aarhus
Fabrik Berlin GmbH, Berlin
Ejendomsselskabet Jægersborggade ApS
Pixelwork Ltd
Holm Foto ApS
Netværk Nord A/S
Kontorland A/S
How Folio is built
The books are a log of events that is only ever appended to: an entry, an invoice, a bill, a reversal, a bank file, a match and a close. Each laptop keeps the log in IndexedDB and works out the entries, the reports and the VAT return from it, so it opens and posts with no network. A service worker keeps the app itself.
An event’s id is the laptop and its own sequence number, such as Anna:12. The laptop sends the events it made with the number of events it already has from the server, and gets back what was taken, what was refused and why, and every event after its number. The server stores an id once, so an event sent again after a lost answer counts once.
The server keeps the one order of events. A laptop shows the server’s events in that order, then its own waiting ones. After a sync both laptops have the same log, so the same voucher and invoice numbers, the same trial balance and the same VAT return.
To correct an entry, Folio posts the entry that reverses it and the corrected one, as one event. Two laptops can therefore never overwrite each other: two entries made offline both arrive, and a second reversal of the same entry is refused with the name of who reversed it first.
Every check is the same code on the laptop and on the server (books.mjs): an entry must balance in whole øre, a VAT line must be 25 % of its line, an invoice or a bill is never paid more than is open, a bank line is matched once, and a closed quarter takes no entry. When two laptops match one bank line offline, the server takes the first to arrive and tells the other.
A bank file is read as Danske Bank, Nordea and most Danish banks export it: semicolons, Danish dates and amounts, Windows-1252 or UTF-8. A line’s id comes from its date, amount, text and balance, so two overlapping files or the same file on two laptops give one set of lines. The matcher scores each open invoice, bill, payroll and VAT settlement by the amount, a number or a name in the text, and the date against the due date, and shows the reasons. A person confirms.
The VAT return has the boxes of the Danish form. Each box is worked out from the VAT lines, and checked against the base lines and against the accounts. Closing a quarter posts the settlement to account 6950 and locks the quarter.
The proof: a property test with its own seeded random generator runs thousands of random operations on two laptops, with offline periods and syncs in random order, and checks after every step that every entry balances, the trial balance sums to zero, the VAT return equals the VAT postings, a locked quarter never changes, nothing counts twice and every change is kept or refused with a reason, and at the end that both laptops hold the server’s entries. Four flawed versions show that the test finds each kind of mistake.