Own project · 2026
Threshold Watcher
A service that adds up income from Invoice Ninja and receipts from Paperless together with the taxable pay on the payroll statement, and warns before a threshold breaks.
Concept, development and operation
Built with
Python · FastAPI · SQLite · pdftotext · Docker
The problem
Anyone self-employed alongside a job hangs on several thresholds at once: the small business threshold (Kleinunternehmergrenze), the marginal earnings threshold (Geringfügigkeitsgrenze) and the obligation to file an income tax return (Veranlagungspflicht). The numbers for that sit in three places: open and paid invoices in Invoice Ninja, receipts in Paperless, and the taxable pay under line 245 (Kennzahl 245) of the payroll statement. Nobody works that out by hand every month, and it only comes to light in the tax assessment.
The solution
A service fetches the current state from Invoice Ninja and Paperless every fifteen minutes, reads new payroll statements with pdftotext and pulls out line 245. The calculation core is stateless: it takes numbers in and gives an assessment back, without storing or loading anything itself. It is tested against examples worked through by hand, so an error in the arithmetic shows up rather than the tax adviser finding it. Persistence and reconciliation sit alongside in SQLite; the interface shows, for each threshold, the distance in euros and the month in which it would be reached if things carry on as they are.
The result
- Every threshold has a current distance in euros instead of an estimate at the end of the year.
- The calculation core runs without state and is backed by cases worked through by hand.
- Payroll statements are read in automatically instead of amounts being typed over.
- The reconciliation runs every fifteen minutes, without anyone starting it.
Why it exists
The thresholds that self-employment alongside a job hangs on are harmless one by one and confusing together. They apply to different periods, to different amounts and to numbers that sit in different systems. The watcher answers a single question: how far is it to the next threshold, and when would it be reached if things carry on as they are?
How it is built
One reconciliation run per source, a calculation core without state, one SQLite file for the history. The core takes numbers as arguments and gives an assessment back, with no access to the database or the network. That makes it checkable: for every case there is a test with a result worked through by hand. Errors in the connections stay errors in the connections and do not distort the arithmetic.
What carries over from it
The same pattern holds anywhere numbers from several systems come together into one decision: reconciliation on the outside, arithmetic on the inside, tests against cases worked through by hand. That is exactly the pattern I follow when building reports for businesses.