mirror of
https://github.com/Zproger/Aurum.git
synced 2026-10-13 11:06:40 +07:00
docs: sync README and DOCS with health version, category delete rules, split transactions
/api/health now returns a version field, default categories can be deleted once unused, and split transactions (dividing one purchase across a parent category's subcategories) were fully undocumented — added a dedicated section in DOCS.md and a mention in README's feature list. Also fixed the "update to newer code" command, which pulled a Docker image tag that doesn't exist for locally-built services instead of the actual git repo.
This commit is contained in:
63
DOCS.md
63
DOCS.md
@@ -116,8 +116,9 @@ string.
|
|||||||
|
|
||||||
### `GET /api/health`
|
### `GET /api/health`
|
||||||
|
|
||||||
No auth required. Returns `{"status": "ok"}`. Use this to check the backend is up before hitting
|
No auth required. Returns `{"status": "ok", "version": "1.1.0"}` — `version` is the running app's
|
||||||
anything else.
|
version, the same value shown in the UI under Settings. Use this to check the backend is up before
|
||||||
|
hitting anything else.
|
||||||
|
|
||||||
## Accounts
|
## Accounts
|
||||||
|
|
||||||
@@ -181,7 +182,7 @@ though they can be renamed, recolored, and reordered.
|
|||||||
| `GET` | `/categories` | List categories, sorted by `sort_order`. `?kind=income` or `?kind=expense` to filter. |
|
| `GET` | `/categories` | List categories, sorted by `sort_order`. `?kind=income` or `?kind=expense` to filter. |
|
||||||
| `POST` | `/categories` | Create a category (or subcategory, via `parent_id`). |
|
| `POST` | `/categories` | Create a category (or subcategory, via `parent_id`). |
|
||||||
| `PATCH` | `/categories/{id}` | Update a category (partial). |
|
| `PATCH` | `/categories/{id}` | Update a category (partial). |
|
||||||
| `DELETE` | `/categories/{id}` | Delete a category. Fails with `400` if it's a default category. |
|
| `DELETE` | `/categories/{id}` | Delete a category. For a default (`is_default: true`) category, fails with `400` if any transaction or transaction split still points at it — a custom category has no such guard and can always be deleted. |
|
||||||
|
|
||||||
**Create body:**
|
**Create body:**
|
||||||
|
|
||||||
@@ -293,7 +294,7 @@ Field rules, enforced server-side:
|
|||||||
`kind` if given (an `income` transaction can't use an `expense` category). `transfer_account_id`
|
`kind` if given (an `income` transaction can't use an `expense` category). `transfer_account_id`
|
||||||
must be omitted.
|
must be omitted.
|
||||||
- `transfer`: `transfer_account_id` is **required** and must differ from `account_id`.
|
- `transfer`: `transfer_account_id` is **required** and must differ from `account_id`.
|
||||||
`category_id` must be omitted — transfers aren't categorized.
|
`category_id` must be omitted — transfers aren't categorized. `splits` aren't valid either.
|
||||||
- `amount`: required, `> 0` (transfers/expenses aren't signed negative — direction comes from
|
- `amount`: required, `> 0` (transfers/expenses aren't signed negative — direction comes from
|
||||||
`type`), up to 14 digits, 2 decimal places.
|
`type`), up to 14 digits, 2 decimal places.
|
||||||
- `description`: required, 1–255 chars. Auto-capitalized server-side (first letter uppercased).
|
- `description`: required, 1–255 chars. Auto-capitalized server-side (first letter uppercased).
|
||||||
@@ -301,13 +302,61 @@ Field rules, enforced server-side:
|
|||||||
- `notes`: optional, free text.
|
- `notes`: optional, free text.
|
||||||
- `date`: required.
|
- `date`: required.
|
||||||
- `tag_ids`: optional list of existing tag IDs. Unknown IDs return `400`.
|
- `tag_ids`: optional list of existing tag IDs. Unknown IDs return `400`.
|
||||||
|
- `splits`: optional list of `{"category_id": ..., "amount": ..., "note": ...}` — divides the
|
||||||
|
transaction across 2+ categories instead of one. See **Splitting a transaction** below.
|
||||||
|
|
||||||
**Update** (`TransactionUpdate`) accepts the same fields, all optional, plus the same
|
**Update** (`TransactionUpdate`) accepts the same fields, all optional, plus the same
|
||||||
type/category/transfer consistency rules applied to the *effective* (merged) values. `tag_ids`: omit
|
type/category/transfer/split consistency rules applied to the *effective* (merged) values. `tag_ids`:
|
||||||
to leave tags untouched; send (even `[]`) to replace the full tag set.
|
omit to leave tags untouched; send (even `[]`) to replace the full tag set. `splits`: omit to leave
|
||||||
|
splits untouched; send (even `[]`) to replace the full split set — send `[]` together with a
|
||||||
|
`category_id` to turn a split transaction back into a normal single-category one.
|
||||||
|
|
||||||
**Response** (`TransactionRead`): the input fields plus `id`, and the nested `account`
|
**Response** (`TransactionRead`): the input fields plus `id`, and the nested `account`
|
||||||
(`AccountRead`), `category` (`CategoryRead` or `null`), `tags` (`TagRead[]`).
|
(`AccountRead`), `category` (`CategoryRead` or `null`), `tags` (`TagRead[]`), `splits`
|
||||||
|
(`TransactionSplitRead[]`, empty for a normal non-split transaction).
|
||||||
|
|
||||||
|
### Splitting a transaction across categories
|
||||||
|
|
||||||
|
A single purchase that covers more than one subcategory of the same parent (a supermarket receipt
|
||||||
|
that's part "Sweets", part "Alcohol", both under "Groceries") can be recorded as one transaction
|
||||||
|
divided across those categories instead of several separate transactions:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"account_id": 1,
|
||||||
|
"type": "expense",
|
||||||
|
"amount": 84.20,
|
||||||
|
"description": "Grocery run",
|
||||||
|
"date": "2026-08-29",
|
||||||
|
"splits": [
|
||||||
|
{ "category_id": 12, "amount": 60.00, "note": "Sweets" },
|
||||||
|
{ "category_id": 13, "amount": 24.20, "note": "Alcohol" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules, enforced on both create and update (against the row as it would look *after* the change):
|
||||||
|
|
||||||
|
- `category_id` on the transaction itself **must be omitted** when `splits` is given — the split
|
||||||
|
entries carry the categories instead.
|
||||||
|
- Not valid on a `transfer` transaction.
|
||||||
|
- At least 2 entries — a single split is just `category_id` with extra steps and is rejected.
|
||||||
|
- Each split's `category_id` is required and must be a category of the transaction's own kind
|
||||||
|
(`income`/`expense`, same check a plain `category_id` gets); `amount` is required (`> 0`, up to 14
|
||||||
|
digits/2 decimals); `note` is optional, up to 200 chars.
|
||||||
|
- **All splits must share the same top-level category** — either the parent itself (an
|
||||||
|
unspecified-subcategory line) or one of its direct subcategories. Splitting across two unrelated
|
||||||
|
top-level categories (e.g. part "Groceries", part "Transport") is rejected with `400`, since the
|
||||||
|
feature exists to divide one purchase's total *within* one category tree, not to record what's
|
||||||
|
really two separate transactions as one.
|
||||||
|
- The split amounts **must sum exactly** to the transaction's own `amount`, or the request is
|
||||||
|
rejected with `400`.
|
||||||
|
|
||||||
|
Aggregation endpoints are split-aware: `/dashboard/summary`'s `spending_by_category` and
|
||||||
|
`/reports/category-ranking` count a split transaction's amount under every category it actually
|
||||||
|
touches (rolled up to the shared parent), not just once under a single category — a receipt split
|
||||||
|
$60/$24.20 between Sweets and Alcohol above contributes exactly those amounts to each subcategory's
|
||||||
|
total under Groceries.
|
||||||
|
|
||||||
### Bulk create (`POST /transactions/bulk`)
|
### Bulk create (`POST /transactions/bulk`)
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ Aurum treats your financial life as a **system**, not a spreadsheet.
|
|||||||
## 🧩 Core Features
|
## 🧩 Core Features
|
||||||
|
|
||||||
### 💰 Cash Flow & Transactions
|
### 💰 Cash Flow & Transactions
|
||||||
Log income, expenses, and transfers across as many accounts as you want, organized into categories (with one level of subcategories) plus free-form tags, and searchable. Already have history elsewhere? Import a bank's CSV export in a guided 3-step wizard instead of typing every line by hand. A dedicated Cash Flow view charts income vs. expense month by month.
|
Log income, expenses, and transfers across as many accounts as you want, organized into categories (with one level of subcategories) plus free-form tags, and searchable. One purchase spanning multiple subcategories of the same parent (a grocery receipt part "Sweets", part "Alcohol") can be recorded as a single split transaction instead of several — every report and chart still counts each share under its own category. Already have history elsewhere? Import a bank's CSV export in a guided 3-step wizard instead of typing every line by hand. A dedicated Cash Flow view charts income vs. expense month by month.
|
||||||
|
|
||||||
### 📈 Net Worth Engine
|
### 📈 Net Worth Engine
|
||||||
A live net worth timeline (30 days to all-time) aggregating cash and every manually tracked asset — investments, crypto, real estate, vehicles, precious metals — into one number, with a full breakdown by asset class, by how each asset behaves (income / neutral / drain), and by risk level.
|
A live net worth timeline (30 days to all-time) aggregating cash and every manually tracked asset — investments, crypto, real estate, vehicles, precious metals — into one number, with a full breakdown by asset class, by how each asset behaves (income / neutral / drain), and by risk level.
|
||||||
@@ -157,7 +157,7 @@ docker compose logs web
|
|||||||
docker compose down # stop everything, keep your data
|
docker compose down # stop everything, keep your data
|
||||||
docker compose up -d # start it again later
|
docker compose up -d # start it again later
|
||||||
docker compose down -v # stop AND permanently delete your data — be sure
|
docker compose down -v # stop AND permanently delete your data — be sure
|
||||||
docker compose pull && docker compose up -d --build # update to newer code
|
git pull && docker compose up -d --build # update to newer code
|
||||||
```
|
```
|
||||||
|
|
||||||
Your data lives in a Docker named volume (`aurum_pgdata`), not in the repo folder — it survives `docker compose down`, image rebuilds, and `git pull`. It's only gone if you explicitly run `docker compose down -v` or delete the volume yourself. For anything short of that, use the in-app **Settings → Backup & Restore** to export a JSON snapshot of everything before making risky changes.
|
Your data lives in a Docker named volume (`aurum_pgdata`), not in the repo folder — it survives `docker compose down`, image rebuilds, and `git pull`. It's only gone if you explicitly run `docker compose down -v` or delete the volume yourself. For anything short of that, use the in-app **Settings → Backup & Restore** to export a JSON snapshot of everything before making risky changes.
|
||||||
|
|||||||
Reference in New Issue
Block a user