diff --git a/README.md b/README.md index e00be29..516bcf6 100644 --- a/README.md +++ b/README.md @@ -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( - category=cat, - amount_eur=200.00, # Monthly average -) -set_category_budget(db_session, budget) + # Budgets can also be unbounded (no dates specified) + budget = CategoryBudget( + category=cat, + amount_eur=200.00, # Monthly average + ) + 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 diff --git a/src/domain_utils.py b/src/domain_utils.py index 55e1794..f8ca801 100644 --- a/src/domain_utils.py +++ b/src/domain_utils.py @@ -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) diff --git a/tests/test_domain_utils_budget_overlap.py b/tests/test_domain_utils_budget_overlap.py index 546fbea..dc23bb2 100644 --- a/tests/test_domain_utils_budget_overlap.py +++ b/tests/test_domain_utils_budget_overlap.py @@ -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."""