from datetime import date from decimal import Decimal from app.models.tables import Account, Statement, Transaction from app.services.balances import account_balance H = {"Authorization": "Bearer test-key"} def test_account_crud_and_balance(client, db): r = client.post("/api/accounts", headers=H, json={ "bank": "DKB", "iban": "DE02120300000000202051", "name": "Giro", "type": "giro"}) assert r.status_code == 201 acc_id = r.json()["id"] db.add(Statement(filename="a.pdf", bank="dkb", account_id=acc_id, period_start=date(2026, 6, 1), period_end=date(2026, 6, 30), opening_balance=Decimal("0"), closing_balance=Decimal("100.00"), status="confirmed")) db.add(Transaction(account_id=acc_id, booking_date=date(2026, 7, 2), amount=Decimal("-30.00"), purpose="Bar", status="confirmed", dedup_hash="x1")) db.commit() acc = db.get(Account, acc_id) assert account_balance(db, acc) == Decimal("70.00") def test_manual_transaction_dedup(client): acc = client.post("/api/accounts", headers=H, json={ "bank": "DKB", "iban": "DE99", "name": "G", "type": "giro"}).json() payload = {"account_id": acc["id"], "booking_date": "2026-07-01", "amount": "-10.00", "purpose": "Kaffee", "counterparty": ""} assert client.post("/api/transactions", headers=H, json=payload).status_code == 201 assert client.post("/api/transactions", headers=H, json=payload).status_code == 409 payload["force"] = True assert client.post("/api/transactions", headers=H, json=payload).status_code == 201 def test_rules_categorize(client): cat = client.post("/api/categories", headers=H, json={"name": "Energie"}).json() client.post("/api/category-rules", headers=H, json={"pattern": "stadtwerke", "category_id": cat["id"]}) acc = client.post("/api/accounts", headers=H, json={ "bank": "DKB", "iban": "DE98", "name": "G2", "type": "giro"}).json() client.post("/api/transactions", headers=H, json={ "account_id": acc["id"], "booking_date": "2026-07-03", "amount": "-80.00", "purpose": "STADTWERKE Abschlag", "counterparty": ""}) txs = client.get("/api/transactions", headers=H, params={"account_id": acc["id"]}).json() assert txs[0]["category_id"] == cat["id"] def test_patch_account_name(client): acc = client.post("/api/accounts", headers=H, json={ "bank": "DKB", "iban": "DE71", "name": "DE71", "type": "giro"}).json() r = client.patch(f"/api/accounts/{acc['id']}", headers=H, json={"name": "DKB Giro"}) assert r.status_code == 200 and r.json()["name"] == "DKB Giro" assert client.patch("/api/accounts/9999", headers=H, json={"name": "x"}).status_code == 404 assert client.patch(f"/api/accounts/{acc['id']}", headers=H, json={"name": " "}).status_code == 422 def test_patch_transaction_category(client): cat = client.post("/api/categories", headers=H, json={"name": "Freizeit"}).json() acc = client.post("/api/accounts", headers=H, json={ "bank": "DKB", "iban": "DE77", "name": "G3", "type": "giro"}).json() tx = client.post("/api/transactions", headers=H, json={ "account_id": acc["id"], "booking_date": "2026-07-04", "amount": "-15.00", "purpose": "Kino", "counterparty": ""}).json() tx_id = tx["id"] assert tx["category_id"] is None r = client.patch(f"/api/transactions/{tx_id}", headers=H, json={"category_id": cat["id"]}) assert r.status_code == 200 assert r.json()["category_id"] == cat["id"] r = client.patch(f"/api/transactions/{tx_id}", headers=H, json={}) assert r.status_code == 200 assert r.json()["category_id"] == cat["id"] r = client.patch(f"/api/transactions/{tx_id}", headers=H, json={"category_id": None}) assert r.status_code == 200 assert r.json()["category_id"] is None r = client.patch(f"/api/transactions/{tx_id}", headers=H, json={"category_id": 9999}) assert r.status_code == 404