fix: Neue Tabellen ins Backup, toter Transfer-Code raus
Sieben nutzereigene Tabellen aus den Migrationen 0015-0020 standen in keiner Backup-Liste. Am schwersten wiegt inbox_items: Der Eingang haelt fest, welche Zahlungen von aussen schon erfasst wurden. Fehlt er nach dem Wiedereinspielen, erkennt der naechste Kontoauszug sie nicht wieder und bucht sie ein zweites Mal – genau der Fehler, den die Tabelle verhindern soll. Aufgenommen: inbox_items, transfer_links, link_ignored, api_tokens. Bewusst nicht: mail_sources/mail_senders (tragen das verschluesselte IMAP-Passwort; ein Backup ist eine Datei, die man herumreicht) und pending_uploads (fluechtige Rohdateien). Beides steht begruendet im Code. Ein Test laeuft jetzt ueber Base.registry und fordert fuer jedes Modell mit owner_id eine bewusste Entscheidung – sichern oder begruendet ausnehmen. Genau diese Pruefung fehlte, weshalb die sieben unbemerkt durchrutschten. Dazu der tote Code aus dem Transfer-Ausbau: is_counterpart und braucht_ziel waren feste False, _apply_counterpart_amount damit unerreichbar. Die Tests sichern dieselbe Garantie jetzt ueber das tatsaechliche Verhalten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -766,9 +766,6 @@ def detect_rows(db: Session, user: User, account: Account, candidates: list[dict
|
||||
is_dup = True
|
||||
dup_grund = "inbox"
|
||||
|
||||
is_counterpart = False
|
||||
braucht_ziel = False
|
||||
|
||||
out.append({
|
||||
"index": i,
|
||||
"date": d.isoformat() if d else None,
|
||||
@@ -791,11 +788,9 @@ def detect_rows(db: Session, user: User, account: Account, candidates: list[dict
|
||||
"import_hash": h,
|
||||
"is_duplicate": is_dup,
|
||||
"duplicate_reason": dup_grund,
|
||||
"is_counterpart": is_counterpart,
|
||||
# Damit commit_import die geschätzte Gegenseite auf den echten Wert setzt.
|
||||
"counterpart_txn_id": None,
|
||||
"include": valid and not is_dup and not is_counterpart and not braucht_ziel,
|
||||
"needs_target": braucht_ziel,
|
||||
"include": valid and not is_dup,
|
||||
})
|
||||
return out
|
||||
|
||||
@@ -822,7 +817,7 @@ def reconcile(rows: list[dict]) -> dict:
|
||||
einer früher gebuchten Buchung. Eine Zeile, die auf ein Gegenkonto wartet,
|
||||
zählt dagegen NICHT – sie ist unterwegs verloren, und genau das soll der
|
||||
Abgleich melden."""
|
||||
if not r.get("valid") or r.get("needs_target"):
|
||||
if not r.get("valid"):
|
||||
return 0
|
||||
return r.get("amount_cents") or 0
|
||||
|
||||
@@ -991,34 +986,6 @@ def _track_and_learn(db: Session, user: User, row: dict) -> None:
|
||||
_learn_rule(db, user, row)
|
||||
|
||||
|
||||
def _apply_counterpart_amount(db: Session, user: User, account: Account, row: dict) -> bool:
|
||||
"""Trägt den tatsächlichen Betrag einer Umtausch-Gegenbuchung in den bereits
|
||||
gebuchten Transfer ein. Diese Zeile stammt aus dem Auszug der anderen Seite und
|
||||
nennt exakt, was dort angekommen bzw. abgegangen ist."""
|
||||
txn_id = row.get("counterpart_txn_id")
|
||||
amount = row.get("amount_cents")
|
||||
if not txn_id or not amount or not row.get("is_counterpart"):
|
||||
return False
|
||||
txn = db.get(Transaction, txn_id)
|
||||
if txn is None or txn.owner_id != user.id or txn.type != TxnType.transfer:
|
||||
return False
|
||||
absamt = abs(amount)
|
||||
if txn.to_account_id == account.id: # hier ist das Geld angekommen
|
||||
if txn.to_amount_cents == absamt:
|
||||
return False
|
||||
txn.to_amount_cents = absamt
|
||||
elif txn.account_id == account.id: # hier ist es abgegangen
|
||||
if txn.amount_cents == absamt:
|
||||
return False
|
||||
txn.amount_cents = absamt
|
||||
txn.amount_eur_cents = currency.compute_amount_eur_cents(
|
||||
db, absamt, account.currency, txn.date
|
||||
)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def commit_import(db: Session, user: User, account: Account, template_id: int | None, filename: str,
|
||||
rows: list[dict], preset: str | None = None) -> ImportBatch:
|
||||
accounts = {a.id: a for a in db.query(Account).filter(Account.owner_id == user.id)}
|
||||
@@ -1031,14 +998,8 @@ def commit_import(db: Session, user: User, account: Account, template_id: int |
|
||||
db.flush()
|
||||
|
||||
imported = 0
|
||||
korrigiert = 0
|
||||
for row in rows:
|
||||
if not row.get("include", True):
|
||||
# Gegenbuchung eines Umtauschs: nicht buchen, aber den echten Betrag
|
||||
# dieser Seite in den bestehenden Transfer eintragen. Sonst bliebe dort
|
||||
# die EZB-Schätzung stehen und der Saldo wiche um die Kursdifferenz ab.
|
||||
if _apply_counterpart_amount(db, user, account, row):
|
||||
korrigiert += 1
|
||||
continue
|
||||
txn = _make_transaction(db, user, account, accounts, row, batch.id)
|
||||
if txn is None:
|
||||
|
||||
Reference in New Issue
Block a user