An append-only ledger for a building committee: the entry structure, why the unit is the account, and three balances that must agree every month.
Key takeaways
- Every money movement is a row that is appended and never edited. A correction is a reversing row pointing at the original, and both survive.
- The account is the unit, not the resident. Owners and tenants change; the unit remains the same entity across the whole period.
- Three balances must agree each month: resident debt, the bank balance and vendor liabilities. Any one of them alone says nothing.
- A resident request is not a money movement. It is a case linked to a unit, sometimes to a vendor and a payment, but counted separately.
A building committee changes hands, and the only thing that carries over is the record. A ledger that can be edited is not a record, it is a current version, and nothing about last year can be proved from it. What follows is a technical description of a data structure for collections, vendor payments and resident requests, not legal, accounting or financial advice.
The row that is never edited
An entry in a committee ledger carries seven fields, all written once:
entry_id- a stable running identifier that never changes.unit_id- the apartment. For a shared expense it is empty and anexpense_categorystands in its place.entry_type- charge, receipt, expense, reversal.amountandcurrency- a signed amount, so a plain sum produces a balance.effective_date- the date the movement belongs to, for instance the month collected.recorded_atandrecorded_by- when and by whom it was written. Two fields distinct fromeffective_date.document_ref- a pointer to the evidence: a receipt, a vendor invoice, a transfer confirmation.reverses_entry_id- populated only on reversal rows.
Pairing effective_date with recorded_at is what makes two different questions answerable: what was the balance at the end of March, and what did we know about March's balance at the time. A committee handing over to the next one owes both. The principle of a record that accumulates rather than being overwritten is set out in Martin Fowler on Event Sourcing.
Technical enforcement beats policy: a trigger blocking UPDATE and DELETE on the entries table turns the rule into something nobody can forget, and that syntax is documented in SQLite CREATE TRIGGER.
Why is the unit the account rather than the person?
Because people change and the unit does not. A tenant leaving mid-year, an owner selling, an heir moving in - each changes who pays and none of them changes which unit the charge belongs to.
A structure that holds up:
| Entity | What it identifies | What links to it |
|---|---|---|
| Unit | The physical apartment, a fixed number | Periodic charges, maintenance requests |
| Holder | Who occupies the unit, with a date range | Actual payments, contact details |
| Payment | Who actually paid | Linked to both the unit and the holder |
| Vendor | A service provider | Invoices, payments, requests |
Pairing a unit with a holder's date range is what answers "who owed in May". In a spreadsheet where the resident's name is the key, replacing a tenant either erases the previous one's history or assigns them debt that is not theirs. The same identity problem in a commercial context is discussed in the accounts-receivable collection workflow.
Three balances that must agree
One balance is not enough to know the books are sound. Three checked against one another are:
| The balance | What it derives from | What a mismatch means |
|---|---|---|
| Resident debt | Charges minus receipts, per unit | A receipt captured with no unit assigned |
| Bank balance | The building account statement | A payment made and unrecorded, or the reverse |
| Vendor liabilities | Approved invoices minus payments | An invoice approved and then forgotten |
The monthly check is not looking for confirmation that everything is fine. It is looking for the rows that do not agree, and in most months there are two or three. A receipt landing in the bank with no identifiable reference is the common one, and it should sit in an "unassigned" state until somebody identifies it rather than being attributed to a unit on the strength of a similar amount.
Vendor payments: four documents, not four decisions
Paying a vendor in a building passes through four stages, each with a document and a date:
- Quote - amount, scope of work, validity. Kept even when it was not selected.
- Approval - who approved and when, against that specific quote.
- Invoice - arrives after the work, linked to the approved quote.
- Payment - linked to the invoice, with a transfer reference.
The stage that actually disappears is the first: unsuccessful quotes get deleted, and then there is no way to show that three quotes existed. A quote that was not chosen is exactly as valuable a document as the one that was. The approval-to-payment process itself, in its commercial form, is unpacked in supplier invoice approval through to payment.
How do you prove a resident paid two years ago?
In three parts, all present from day one rather than assembled retroactively.
The first is the receipt row itself, never edited, with its effective_date and recorded_at. The second is the document_ref pointing at the receipt or transfer confirmation as stored. The third is the ability to regenerate that month's report and get exactly the same figures.
The third point is what separates a system from a spreadsheet. If the report is produced from the entries rather than saved as a final file, regenerating it two years later returns an identical result as long as no row was deleted. If the report exists only as a saved file, there is no way to tell whether it matches the ledger. Keep both: the file as circulated, and the ability to produce it again.
Resident requests: a case, not an entry
A report of a leak or a broken light is not money, so it does not belong in the ledger. It is a case with fields of its own:
unit_idor a common area, one of the two.reported_atand who reported it.categoryfrom a closed list: plumbing, electrical, lift, cleaning, other.vendor_idonce it has been referred out.statusfrom opened, referred, done, closed.linked_entries[]- pointers to any money movements it produced.
That last field is the bridge. It answers what lift maintenance cost this year without mixing requests into the charge ledger. The collection channels through which receipts arrive in the first place are discussed in business collection through Bit and PayBox.
What actually breaks
- A correction made as an edit. A mistyped amount is fixed in place, and last month's report stops matching the one that was circulated.
- A receipt with no unit. A transfer arriving with no reference counts as income and closes nobody's debt.
- A resident name as the key. Two residents sharing a surname in one building is enough to merge two histories.
- An invoice approved and never paid. With no vendor liability balance it simply disappears until the vendor calls.
- Cash recorded with no evidence. A row with no document cannot be proved, even when it is correct.
- A handover with no export. An outgoing committee passing on one file rather than the system passes on a snapshot, not a history.
Sources
Frequently asked questions
Does a small building committee really need a ledger?
The structure is needed at any size; the tooling is not. An eight-unit building can run in a spreadsheet where rows are only ever added, there is a reference column, and nobody deletes. What fails at every size is editing old rows, because that erases exactly what a new committee will ask to see.
What do you do about an error found in a charge from six months ago?
Write a reversing row dated today with `reverses_entry_id` pointing at the original, then a new row with the correct amount. Both rows survive. That month's report stays as it was, and the current balance corrects itself without rewriting the past.
How do you stay transparent without exposing neighbours' debts?
By separating what everyone sees from what a resident sees. Aggregate totals and vendor payments sit at building level, while an individual balance is exposed only to its own unit. The same database serves both views; the difference is in the query and the permission.
What has to transfer when a committee changes?
Access to the complete ledger, all linked evidence, the vendor list with its agreements, and the ability to regenerate reports for prior periods. A single summary file is not a handover, because it presents a balance without the route by which it was reached.
Keep reading
Related service
Business Automation
I build custom automations that remove repetitive work end to end.
About the author
Yehonatan Saadia
Freelance automation, web & MVP developer
I'm Yehonatan Saadia, a senior developer who builds business automation, custom websites, and MVPs for small and mid-sized companies across the US, Europe, and Israel. These guides come from real client work, not theory.
Work with meHave a project like this?
Tell me what you're trying to automate or build. I reply within 24 business hours with a few targeted questions, then we walk through it on a free 30-minute call, with no commitment. You come away with a scope, a timeline and a fixed price - or a straight answer that it isn't worth building.
