Compare commits

1 Commits
Author SHA1 Message Date
mathias2784 6babc560c7 Category overhaul (#20)
Tests / lint (push) Successful in 16s
Tests / test (push) Successful in 16s
Reviewed-on: #20
Co-authored-by: Mathias <mathias2784@web.de>
2026-10-27 18:08:10 +00:00
3 changed files with 54 additions and 13 deletions
+21 -8
View File
@@ -23,20 +23,29 @@ alembic upgrade head
```python
from src.ingest.b4w import ingest_b4w
from src.db import get_session
from pathlib import Path
csv_path = Path("/mnt/anbau/Software/Python/finanz-cockpit/scripts/dummy_data/b4w_export_dummy_01.csv")
ingest_b4w(csv_path)
with get_session() as session:
ingest_b4w(csv_path, session)
```
### Import comdirect Export
```python
from src.ingest.comdirect import ingest_comdirect
from src.db import get_session
from pathlib import Path
csv_path = Path("/mnt/anbau/Software/Python/finanz-cockpit/scripts/dummy_data/comdirect_giro_01.csv")
ingest_comdirect(csv_path)
with get_session() as session:
ingest_comdirect(
session,
csv_path,
account_iban="DE89370400440532013000",
account_currency="EUR",
)
```
### Set Category Budgets
@@ -45,6 +54,7 @@ ingest_comdirect(csv_path)
from src.models.category import Category, CategoryBudget
from datetime import date
from src.domain_utils import set_category_budget
from src.db import get_session
# Create category hierarchy
cat = Category.from_str("Lebenshaltung:Essen:Supermarkt")
@@ -56,14 +66,17 @@ budget = CategoryBudget(
valid_from=date(2026, 10, 1),
valid_to=date(2026, 11, 30),
)
set_category_budget(db_session, budget)
with get_session() as session:
set_category_budget(session, budget)
session.commit()
# Budgets can also be unbounded (no dates specified)
budget = CategoryBudget(
# Budgets can also be unbounded (no dates specified)
budget = CategoryBudget(
category=cat,
amount_eur=200.00, # Monthly average
)
set_category_budget(db_session, budget)
)
set_category_budget(session, budget)
session.commit()
```
## Testing
@@ -141,7 +154,7 @@ alembic upgrade head
pytest
# Run with coverage report
pytest --cov=src --cov-report=html -c .coverage htmlcov
pytest --cov=src --cov-report=html:htmlcov
```
### Common Patterns
+2 -1
View File
@@ -29,7 +29,8 @@ def find_category_db(session: Session, category: Category | None) -> CategoryDb
return (
session.query(CategoryDb)
.filter_by(name=node.name, parent_id=parent_id)
.first()
.filter(CategoryDb.valid_to.is_(None))
.one_or_none()
)
return find_existing(category)
+29 -2
View File
@@ -1,13 +1,13 @@
"""Comprehensive tests for set_category_budget overlap handling."""
from datetime import date
from datetime import UTC, date, datetime
import pytest
from sqlalchemy.orm import Session
from src.domain_utils import find_category_db, set_category_budget
from src.models import Category, CategoryBudget, CategoryBudgetDb
from src.models.category import upsert_category
from src.models.category import CategoryDb, upsert_category
from tests.factories import CategoryFactory
# NOTE: MYPY errors suppressed for test fixtures - see AGENTS.md for type checking notes.
@@ -615,6 +615,33 @@ class TestFindCategoryDb:
result = find_category_db(db_session, Category(name="NonExistent"))
assert result is None
def test_find_reintroduced_category_returns_active_version(
self, db_session: Session
):
category = Category(name="Reintroduced")
historical = CategoryDb(
name=category.name,
valid_to=datetime(2026, 1, 1, tzinfo=UTC),
last_action="retired",
)
db_session.add(historical)
db_session.commit()
active = upsert_category(db_session, category, do_commit=False)
assert active is not None
db_session.commit()
result = find_category_db(db_session, category)
assert result is not None
assert result.id == active.id
set_category_budget(
db_session,
CategoryBudget(category=category, amount_eur=100.00),
)
budget = db_session.query(CategoryBudgetDb).one()
assert budget.category_id == active.id
class TestSetCategoryBudgetErrorHandling:
"""Test error handling in set_category_budget."""