Your taxation model
A taxation model is how one account is taxed, written down by you. It exists for one figure: what you would owe if you sold today, on the account's own panel.
Nothing is declared to anybody, nothing is sent anywhere, and this is not a tax return. It is your own statement of your own wrapper, applied to today's prices.
An account carrying no model is ordinary. Most installs start that way, the
seeded default account has none, and the app shows no card at all rather than
guessing a rate — the bell carries one advisory per such account, with a link
back to it.
Where you write it
On the accounts page, on the account's own panel, in the same form that carries the account's name. There is no settings card for it and no page of its own: a model is written where it is attached, so declaring an account and answering the tax question are one gesture.
The field is a list, and it opens on No model. Below that sit the models you have already written — a model is reusable across accounts, so a household with two compte-titres writes one model and picks it twice — and last, Write a model…, which opens the editor.
Removing a model an account still carries is refused, and the message names the accounts. Detach it there first. Removing it from an account is a separate gesture on that account's panel, and it leaves the model itself alone.
The app ships no rates
You type the money. Every rate on this page is one you looked up and entered, and none of them is in the image.
That is deliberate. A rate schedule changes, differs by country, and inside one country differs by the situation of the person reading it; a rate shipped in a container is a tax fact that goes stale the first January nobody pulls a new image, and it would go stale silently, under a figure that still looks computed. The app would be asserting something about your taxes that it has no way to keep true.
What does ship is structure, because structure is what does not move. Two wrappers are offered as a shortcut in the editor:
| Shortcut | What it fills in |
|---|---|
| Plan d'épargne en actions (PEA) — France | a threshold at 5 years, counted from the first payment |
| Assurance-vie — France | a threshold at 8 years, counted from the day the contract was opened |
A shortcut fills two fields of the form and is not stored: pick one, the threshold and its basis land in the editor, and from there it is your model like any other. The rates stay empty. None of these — I will enter it myself is the option for every other wrapper, and it is not a lesser path: the shortcut list is short because only a wrapper whose structure is a stable fact belongs in it.
A rate is typed as a percentage. 12,8 is what a tax schedule says, so
12,8 is what the form takes.
The five shapes
The shape is the first thing the editor asks, and it decides which fields follow. There are five, and the list is closed.
| Shape | What it charges | What you type |
|---|---|---|
| Not taxed | nothing — an exempt holding, or an account you would rather the app said nothing about | nothing |
| One rate on the gain | the gain, when you sell, at one rate | a rate, and social charges if your wrapper has them |
| A rate that changes with the wrapper's age | the gain, at one rate before the wrapper reaches a certain age and another after it | both rates, the threshold in years, what the age is counted from, and social charges if any |
| Brackets on the gain | the gain by tranche, each bracket taxing only the part that falls inside it | the ladder, bottom to top |
| A withholding on income | what you receive — a dividend, a coupon — never a gain | a rate |
Social charges are folded into the rate the card names. Where your model carries them, the figure and the rate shown under it both include the levy — naming the income rate alone would contradict the figure directly above it. On a shape that changes with age, the levy applies on both sides of the threshold: a mature PEA is exempt of income tax and still owes its levy.
The ladder's top rung has no ceiling. And above is the last row and it carries a rate only, which is what makes the ladder total: a set of bounded brackets says nothing about a gain larger than the highest of them, and the app would have to invent a rate for it.
Two shapes state no figure, and they say so in words. Not taxed shows
0.00, and that zero is the model saying so rather than something the app
computed. A withholding on income shows no figure at all: it is charged on
what you receive, so there is nothing about a gain for it to project.
What the age is counted from
Only the aged shape asks, and the two answers are not the same date. A PEA's five years run from its first payment; an assurance-vie's eight run from the day the contract was opened. The two coincide often enough to hide the mistake and not often enough to make guessing safe, which is why the form asks rather than derives.
The day the wrapper was opened. The form then asks for an Opened on date, and that declared date is the only one used. Declare no date and there is no figure — your first payment is not an opening, and the app will not substitute one for the other. The field is offered pre-filled with the date of the first payment on record; correct it if the wrapper is older than this ledger, which is what a transfer from another broker looks like.
The first payment into it. The form does not ask for a date at all: the ledger already holds one, and the earliest payment on record is what the threshold runs from. This is what makes the PEA shortcut work end to end without a single date being typed.
The rate changes on the anniversary, not the day after, and the card names that day for as long as the threshold is still ahead. A wrapper opened on 29 February reaches its threshold on the 28th in a year that has no 29th.
What the app refuses
A model is checked when it is written, once, so what comes back out of the store is what went in. Each refusal below leaves the editor open with the message, and nothing is written.
| What you wrote | Why it comes back |
|---|---|
| a required field left empty | the shape needs it; the optional one is social charges, and only on the two shapes that list it |
| a rate outside 0 – 100 % | a rate is a fraction of something, in both directions |
| a rate and social charges summing past 100 % | 90 % beside 90 % is two ordinary-looking entries that project 180 % of a gain, and a gain cannot owe more than itself |
| a threshold that is not a whole number of years, or is negative | an age threshold is counted in years |
| a threshold above 200 years | no schedule surveyed goes past thirty; the bound is what keeps a typo out of date arithmetic, where an anniversary a hundred thousand years out overflows the calendar rather than producing a wrong answer |
| a ladder with no bracket at all | there is nothing to walk |
| a first ceiling at or below zero | the slice under it would be negative, and a negative slice is a negative tax on a real gain |
| ceilings that do not climb | the ladder is read bottom to top, and each rung taxes what falls between it and the one below |
| an upper bound on the top bracket, or a missing one on any other | only the top rung is open, and it is what every gain above the one below it is taxed at |
| something that is not a number where a number goes | Infinity and NaN included — Python's own JSON reader accepts both, and no tax schedule contains either |
A stored model this version would refuse publishes no figure. Bounds are added over time, and a row written before one existed is read back, checked against today's rules, and left without a projection rather than projected from a value the app no longer accepts. Reopen the editor on it, correct it, and saving tells you which bound it met.