feat: Gleich aussehende Buchungen stehen in der Arbeitsliste
Bisher fand man Dubletten nur zufaellig beim Durchscrollen der Buchungsliste – oder ich habe ein Skript dafuer geschrieben, und der Nutzer musste sich auf einen Server einloggen, um seine eigenen Zahlen zu reparieren. Beides ist falsch. Die Arbeitsliste zeigt sie jetzt: gleiches Konto, gleicher Tag, gleicher Betrag, gleicher Text. Je Zeile steht die Kennung der Bank dabei, und die entscheidet die Frage – dieselbe Kennung heisst sicher dieselbe Zahlung, verschiedene heissen, die Bank hat sie als verschieden gemeldet. Entschieden wird bewusst NICHTS von selbst. Drei Aufrundungen ueber 1.00 am selben Tag sind echt, drei gleiche Einkaeufe koennen es auch sein; das weiss nur der Nutzer. Certain meldet und laesst loeschen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -159,6 +159,54 @@ def list_transactions(
|
||||
return [to_out(db, t, base, ccy, woher) for t in rows]
|
||||
|
||||
|
||||
@router.get("/dubletten")
|
||||
def duplicates(db: Session = Depends(get_db), user: User = Depends(get_current_user)) -> dict:
|
||||
"""Buchungen, die gleich aussehen: gleiches Konto, gleicher Tag, gleicher
|
||||
Betrag, gleicher Text.
|
||||
|
||||
Entschieden wird hier NICHTS. Drei Aufrundungen über 1.00 am selben Tag
|
||||
sind echt, drei gleiche Einkäufe können es auch sein – das weiss nur der
|
||||
Nutzer. Deshalb steht zu jeder Zeile die Kennung der Bank dabei: Tragen
|
||||
zwei Zeilen dieselbe, ist es sicher dieselbe Zahlung; tragen sie
|
||||
verschiedene, hat die Bank sie als verschieden gemeldet.
|
||||
"""
|
||||
from ..services.csv_import import normalize_text
|
||||
|
||||
gruppen: dict[tuple, list] = {}
|
||||
for txn in db.query(Transaction).filter(
|
||||
Transaction.owner_id == user.id,
|
||||
Transaction.type != TxnType.transfer,
|
||||
).order_by(Transaction.date.desc(), Transaction.id):
|
||||
schluessel = (txn.account_id, txn.type.value, txn.date,
|
||||
txn.amount_cents, normalize_text(txn.payee or txn.description or ""))
|
||||
gruppen.setdefault(schluessel, []).append(txn)
|
||||
|
||||
verdaechtig = {k: v for k, v in gruppen.items() if len(v) > 1}
|
||||
if not verdaechtig:
|
||||
return {"gruppen": []}
|
||||
|
||||
alle_ids = [t.id for zeilen in verdaechtig.values() for t in zeilen]
|
||||
herkunft = _herkunft(db, user, [t for zeilen in verdaechtig.values() for t in zeilen])
|
||||
kennungen = {
|
||||
txn_id: (wert or "").split(":", 1)[-1]
|
||||
for txn_id, wert in db.query(InboxItem.transaction_id, InboxItem.external_id)
|
||||
.filter(InboxItem.owner_id == user.id, InboxItem.transaction_id.in_(alle_ids))
|
||||
}
|
||||
return {"gruppen": [
|
||||
{
|
||||
"account_id": k[0], "datum": k[2].isoformat(), "amount_cents": k[3],
|
||||
"typ": k[1],
|
||||
"zeilen": [
|
||||
{"id": t.id, "payee": t.payee, "description": t.description,
|
||||
"category_id": t.category_id, "herkunft": herkunft.get(t.id, "manuell"),
|
||||
"kennung": kennungen.get(t.id)}
|
||||
for t in zeilen
|
||||
],
|
||||
}
|
||||
for k, zeilen in sorted(verdaechtig.items(), key=lambda p: p[0][2], reverse=True)
|
||||
][:100]}
|
||||
|
||||
|
||||
@router.post("", response_model=TransactionOut, status_code=status.HTTP_201_CREATED)
|
||||
def create_transaction(
|
||||
payload: TransactionCreate, db: Session = Depends(get_db), user: User = Depends(get_current_user)
|
||||
|
||||
Reference in New Issue
Block a user