Solid-compatible personal finance insight applet for solid-panes framework
The experiments of the Solid Money-Pane have evolved to what is now the Prejournal project which is no longer strongly tied to Solid.
Please check out SolidBase, ValueFlo.ws and Mikorzal instead, which may currently be closer to what you're looking for.
NOTE:
We have moved our books from our own custom HalfLedger
format to the
more commonly used Plain Text Accounting
standard (specifially, the HLedger dialect).
This has many advantages, for instance, we get to profit from the existing ecosystem data conversion tools from/to the [H]Ledger format, as well as (obviously) the existing reporting tools themselves.
Also, it will make our work more useful since whatever we build (e.g. importers for CSV formats from specific banks) will be usable by anybody who uses plain text accounting.
We are only switching the file format, not moving away from the 'store your personal data on your personal data store' philosophy of Solid. Since your books would generally not be shared directly with others using WAC, this could also be a local data store (your laptop), it doesn't matter that much if it's not an online data store. For this same reason it doesn't matter that much whether we access the data using Solid-CRUD or just straightforward filesystem access (specifically since the most common edit operation would be a lexical append). We could still accept RDF documents into the pod's inbox, for instance when someone submits an expense, receipt, timesheet, etc from inside some Solid app. That's a parser we could add to the existing ecosystem of Plain Text Accounting parsers. Turtle is not as "plain" as CSV, but it's after all pretty close in the sense of being both simple to read and easy edit by hand. :)
One first downside is that we no longer have a data format that is self-describing in a machine-readable way (in RDF, the link to the ontology acts as a machine-readable identifier of the data format, whereas in Plain Text Accounting files, it would be best practice to add a human-readable comment at the top describing the precise file format version. But maybe we can at some point (help to) formalize this notion and teach our machines to read such a file format version comment.
The other downside is we can't as easily include hyperlink references to other data in a meaningful way. There may be a way to do something like that in Plain Text Accounting which we haven't discovered yet (at the least, you would expect account names and currency names to act as globally unique identifiers). Maybe we'll find a way to bring this part of the RDF philosophy to the Plain Text Accounting world.
For now, consider this code as deprecated (with, for now, the exception of ./exportToHLedger.js
).
You can build with npm install && npm run build && cd dist && npx serve
.
You can debug with VSCode + Chrome (see .vscode/launch.json
).
Currently supported csv-file formats:
- ASN Bank, The Netherlands
- Please add yours!
You can deploy this code as a stand-alone Solid app. The way to do that depends on your html-app hosting provider. For instance, to deploy to https://solid-money.5apps.com/ you would:
git checkout deploy # this branch has dist/ commented out in .gitignore
git merge master
npm ci
npm run build
git add dist/
git commit --no-verify -am "build"
git remote add 5apps git@5apps.com:michiel_solid-money.git
git push 5apps deploy:master
You can run:
mkdir data
ln -s path/to/your/data/root.js data/root.js
./node_modules/.bin/ts-node run.ts
Data root format:
{
hours: {
2021: ...
},
invoices: {
2021: ...
},
myIbans: [
'some-iban...',
],
files: {
'path/to/file.csv': {
parser: 'asnbank-mt940',
account: 'some-iban...',
},
},
mcc: {
'0763': 'Groceries',
},
incassant: {
'some=iban...': 'House',
},
iban: {
'some=iban...': 'Transport',
},
description: {
'TIN RABBIT >NEEDHAM': 'Stuff',
},
transactionType: {
NDIV: 'Services',
},
budget: {
Transport: 200/30, // daily budget amount in main currency e.g. euros
},
months: {
'2021-09',
}
}
To see a report of your personal spending habits against your self-imposed budget.
I (Michiel de Jong) am running this with my own data root file now, ping me in
https://gitter.im/federatedbookkeeping/community
if you want to know more about its format, I can
create an up-to-date anonymized example data root file.
## Some notes about the current data format
(subject to change)
### Tracking the arms of the Y
Entries from an imported bank statement is interpreted with the Y-model:
Arrivals Departures \ / * (bank account) | me
A debit entry implies one mutation from `Arrivals` (for instance another IBAN bank account) to `* (bank account)` and another mutation from `* (bank account)` to `me` (account balance increases).
A credit entry implies one mutation from `me` to `* (bank account)` (account balance decreases) and another mutation from `* (bank account)` to `Arrivals` (for instance another IBAN bank account).
In the `AccountHistoryChunk#mutations: WorldLedgerMutation[]` we track the "arms" of the Y, so only the
mutations from `Arrivals` (for instance another IBAN bank account) to `* (bank account)` and from `* (bank account)` to `Arrivals` (for instance another IBAN bank account),
*not* the mutations between `* (bank account)` and `me`.
This may seem unnatural since the entries describe mutations that change the balance between `* (bank account)` and `me`. But each can be derived from the other, it doesn't
seem useful to store both, the arrivals and departures contain more information, and for matching mutations-to-self (e.g. savings account to current account) we already need
to list out the arrivals and departures, so for now we decided to leave out the mutations between bank account and customer. It can of course be reconstructed at any time
using a method like `AccountHistoryChunk#getAccountMutations`.
# Equity Graph
To generate the equity graph:
```sh
node makeBooks.js > ./data/books.js
npx serve
Then visit http://localhost:5000/chart
The script `makeBooks.js converts from the data/expenses.js format to the ./books.js format.
The data in books.js is optimized for displaying the equity graph, and takes the form:
{ seriesLiquid, seriesLiquidCredit, seriesLiquidCreditAssets, step }
Here step
is the number of days between two dots plotted in the graph (for instance 5).
The other items are arrays of numbers.
The file data/transactions.js should have a default export that is an array of objects (transactions). Use these when a client pays an outstanding invoice. The amount is added to 'liquid' and substracted from 'credit'.
{
date: '3 sep 2021',
description: 'customer ABC paid invoice XYZ',
amount: 123.45
}
The file data/expenses.js should have a default export that is an array of objects (expenses).
{
description: 'laptop',
file: 'expenses/laptop-michiel.pdf',
date: '4 jul 2020',
writeOffStart: '4 jul 2020',
writeOffEnd: '4 jul 2024', // write off ~ 150 eur per year
assetGroup: 'computer equipment',
excl: 593.43,
vat: 124.62,
from: 'nl',
incl: 718.05
},
- if
writeOffStart
is missing, default it todate
- if
writeOffEnd
is missing, default it todate
- if
fooi
is missing, default it to 0 - if
vat
is missing, default it to 0 - if
excl
is missing, default it toincl
- if
incl
is missing, default it tosalary
- amounts (i.e.
excl
,vat
,incl
,fooi
,salary
) can be a Float like 124.62 or a string like 'usd 15.16' from
can benl
orEU
ornon-EU
- dates are of the format '9 aug 2021'
writeOffStrategy
'monthly' means:writeOffStartDate
:=date
;writeOffEndDate
:=date
+ 1 monthwriteOffStrategy
'yearly' means:writeOffStartDate
:=date
;writeOffEndDate
:=date
+ 1 year
- increase debt of
excl+fooi
ondate
- increase value of
excl+fooi
in assetGroup ondate
- increase
vat
to ask back - schedule write-off from assetGroup to /dev/null over
writeOffStart -> writeOffEnd