95 lines
4.7 KiB
Markdown
95 lines
4.7 KiB
Markdown
# any2anexoj
|
|
|
|
[](https://coveralls.io/github/nmoniz/any2anexoj?branch=main)
|
|
|
|
<p align="center">
|
|
<img src="https://i.ibb.co/0yRtwq2C/0-FBA40-FD-D97-A-4-AFB-8618-49582-DB98-F3-C.png" alt="Screenshot" border="0">
|
|
</p>
|
|
|
|
This tool converts the statements from known brokers and exchanges into a format compatible with section 9 from the Portuguese IRS form: [Mod_3_anexo_j](https://info.portaldasfinancas.gov.pt/pt/apoio_contribuinte/modelos_formularios/irs/Documents/Mod_3_anexo_J.pdf)
|
|
|
|
> [!WARNING]
|
|
> Although I made significant efforts to ensure the correctness of the calculations you should verify any outputs produced by this tool on your own or with a certified accountant.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
go install github.com/nmoniz/any2anexoj/cmd/any2anexoj-cli@latest
|
|
```
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
cat statement.csv | any2anexoj-cli --platform=tranding212
|
|
```
|
|
|
|
Pass `--format html` to emit a complete, standalone Bootstrap-styled HTML report (a `<!DOCTYPE html>` document with the Anexo J table embedded and Bootstrap loaded via CDN); the default `--format table` prints the pretty terminal table and `--format csv` writes the Anexo J CSV schema.
|
|
|
|
### Incremental Reports
|
|
|
|
For year over year reporting, pass `--state-file <path>` to keep the FIFO buy-queue between runs. The typical workflow is:
|
|
|
|
1. Generate the first year Anexo J report from the first year broker statement and keep the state file.
|
|
2. Next year, generate the next report from the second year broker statement **only**, using the same state file.
|
|
|
|
```bash
|
|
# first year
|
|
cat 2024-statement.csv | any2anexoj-cli --platform=trading212 --state-file=trading212-state.json
|
|
|
|
# following year
|
|
cat 2025-statement.csv | any2anexoj-cli --platform=trading212 --state-file=trading212-state.json
|
|
```
|
|
|
|
The first run behaves like a normal run and creates the state file.
|
|
On later runs the tool loads the previously saved buy queues per symbol (including partially filled lots), processes only the new transactions from the input, and writes the updated state.
|
|
The state file is a JSON document.
|
|
|
|
> [!IMPORTANT]
|
|
> Each new input must contain **only** the new transactions, not the previous ones.
|
|
> The state file already holds the unmatched buy lots from earlier runs, so including previous trades again will cause them to be processed twice and may fail with insufficient bought volume.
|
|
|
|
State is only persisted once the input reaches EOF, so a crash or `Ctrl-C` before the end of input will discard progress from that run.
|
|
|
|
## Rounding
|
|
|
|
All Euro values are rounded to cents (2 decimal places) but internal calculations use the statement values with full precision.
|
|
There are no explicit rules or details about how to round Euro values in Anexo J.
|
|
This application rounds according to `Portaria n.º 1180/2001, art. 2.º, alínea c) e d)` (Ministerial Order / Government Order) examples, which imply we should round to the 2nd decimal place by rounding up (ceiling) or down (floor) depending on whether the third decimal place is ≥ 5 or < 5, respectively.
|
|
|
|
## Web app
|
|
|
|
A minimal single-user web frontend is bundled in the `cmd/any2anexoj-web` binary. It reuses the existing `internal/trading212` parser, the `internal.BuildReport` pipeline, and the `internal/prettyprinter` formatter, so the rendered table is byte-for-byte identical to the CLI output.
|
|
|
|
### Run
|
|
|
|
```bash
|
|
go run ./cmd/any2anexoj-web
|
|
```
|
|
|
|
The server listens on `:8080` by default. Override the port with the `PORT` environment variable:
|
|
|
|
```bash
|
|
PORT=9000 go run ./cmd/any2anexoj-web
|
|
```
|
|
|
|
Then open <http://localhost:8080> in a browser, pick a Trading212 CSV file, and click **Upload**. The rendered Anexo J table appears below the form along with the row count.
|
|
|
|
### API
|
|
|
|
The web app exposes two JSON endpoints. Auth is intentionally stubbed (a no-op middleware), and state is kept in process memory only — single user, no persistence.
|
|
|
|
| Method | Path | Description |
|
|
|--------|----------------|---------------------------------------------------------------------------------------------------|
|
|
| POST | `/api/upload` | `multipart/form-data` with a `csv` file field. Returns `{"table": "<rendered>", "rows": <int>}`. |
|
|
| GET | `/api/table` | Returns the most recently rendered table (`{"table": "", "rows": 0}` if none uploaded yet). |
|
|
|
|
Example:
|
|
|
|
```bash
|
|
curl -F csv=@trading212-small.csv http://localhost:8080/api/upload
|
|
curl http://localhost:8080/api/table
|
|
```
|
|
|
|
> [!NOTE]
|
|
> The web app is single-user and in-memory. Uploads overwrite the previously stored table, and restarting the server clears all state. Auth is a stub — do not expose this service to the public internet without adding real authentication and per-user state first.
|