feat: Gleich aussehende Buchungen lassen sich behalten, statt loeschen zu muessen

Die Liste sagt ausdruecklich, dass sie nichts entscheidet: Drei
Aufrundungen ueber 1.00 am selben Tag sind echt, drei gleiche Einkaeufe
koennen es auch sein – das weiss nur der Nutzer. Dann darf sie aber auch
nicht zu einer Entscheidung draengen, und genau das tat sie: Der einzige
Knopf war der Papierkorb. Wer die Meldung loswerden wollte, musste eine
richtige Buchung wegwerfen.

Jetzt steht neben jeder Gruppe "Alle behalten". An den Buchungen aendert
sich dabei nichts; es ist ein Haken an der Frage.

Der Haken gilt dem, was man gesehen hat – nicht allem, was noch kommt:
Eine Gruppe faellt erst weg, wenn ALLE ihre Zeilen ihn tragen. Kommt
spaeter eine dritte gleich aussehende Buchung dazu, steht die Frage
wieder da, und zwar fuer alle drei. Ein Test haelt das fest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-15 10:33:44 +02:00
co-authored by Claude Opus 5
parent 6fc2a924d2
commit b867b272ba
8 changed files with 156 additions and 0 deletions
+3
View File
@@ -239,6 +239,9 @@ export const api = {
request(`/categories/keywords/anwenden?vorschau=${vorschau}`, { method: "POST" }),
// Buchungen, die gleich aussehen – gemeldet, nicht entschieden.
duplicateTransactions: () => request("/transactions/dubletten"),
// Beide behalten: Die Frage ist beantwortet, die Buchungen bleiben.
keepDuplicates: (ids) =>
request("/transactions/dubletten/behalten", { method: "POST", body: { ids } }),
transactionLinks: () => request("/transactions/links"),
// Nur fragen, nichts tun – fuer Ansichten, die bloss anzeigen wollen,
// dass hier ein Uebertrag stecken koennte.
+17
View File
@@ -30,6 +30,19 @@ export default function Dubletten({ accountById, onChange }) {
}
useEffect(() => { laden(); }, []);
// Ohne diesen Weg blieb nur Loeschen, um die Meldung loszuwerden – die
// Liste draengte damit zu einer Entscheidung, die sie selbst ausdruecklich
// nicht treffen will.
async function behalten(gruppe) {
try {
await api.keepDuplicates(gruppe.zeilen.map((z) => z.id));
laden();
onChange?.();
} catch (e) {
toast(e.message, "error");
}
}
async function loeschen(zeile) {
if (!(await confirm({
title: t("dupes.delete"),
@@ -66,6 +79,10 @@ export default function Dubletten({ accountById, onChange }) {
accountById[g.account_id]?.currency)}
</strong>
{" · "}{t("dupes.count", { n: g.zeilen.length })}
{" "}
<button className="btn sm" onClick={() => behalten(g)}>
<Icon name="check" size={14} /> {t("dupes.keepAll")}
</button>
</div>
{g.zeilen.map((z) => (
<div className="dubletten-zeile" key={z.id}>
+1
View File
@@ -839,6 +839,7 @@
"title": "Gleich aussehende Buchungen ({{n}})",
"intro": "Diese Zeilen sehen aus wie dieselbe Zahlung – gleiches Konto, gleicher Tag, gleicher Betrag, gleicher Text. Entscheiden musst du: Steht überall dieselbe Kennung der Bank, ist es sicher dieselbe Zahlung. Stehen verschiedene, hat die Bank sie als verschieden gemeldet – dann können es auch wirklich mehrere sein.",
"count": "{{n}} mal",
"keepAll": "Alle behalten",
"delete": "Buchung löschen",
"deleteConfirm": "Diese Buchung wird gelöscht. Kam sie von der Bank, holt kein späterer Abruf sie zurück – der Umsatz gilt dann als erledigt.",
"noRef": "keine Kennung"
+1
View File
@@ -839,6 +839,7 @@
"title": "Transactions that look alike ({{n}})",
"intro": "These rows look like the same payment – same account, same day, same amount, same text. You decide: if they all carry the same bank reference it is certainly the same payment. Different references mean the bank reported them as different.",
"count": "{{n}} times",
"keepAll": "Keep all",
"delete": "Delete transaction",
"deleteConfirm": "This transaction will be deleted. If it came from the bank, no later fetch will bring it back – it counts as handled.",
"noRef": "no reference"