import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { api } from "../api"; import Icon from "./Icon"; import { useToast } from "../toast"; import { useConfirm } from "../confirm"; import { formatDate, formatMoney } from "../format"; // 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: Dieselbe Kennung heisst // sicher dieselbe Zahlung, verschiedene heissen, die Bank hat sie als // verschieden gemeldet. export default function Dubletten({ accountById, onChange }) { const { t } = useTranslation(); const toast = useToast(); const confirm = useConfirm(); const [gruppen, setGruppen] = useState(null); async function laden() { try { setGruppen((await api.duplicateTransactions()).gruppen || []); } catch { setGruppen([]); // still ignorieren – nicht kritisch } } 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"), message: t("dupes.deleteConfirm"), danger: true, confirmLabel: t("dupes.delete"), }))) return; try { await api.deleteTransaction(zeile.id); await laden(); onChange?.(); } catch (e) { toast(e.message, "error"); } } if (!gruppen?.length) return null; return (

{t("dupes.title", { n: gruppen.length })}

{t("dupes.intro")}

{gruppen.map((g) => (
{formatDate(g.datum)} · {accountById[g.account_id]?.name || "—"} ·{" "} {formatMoney(g.typ === "income" ? g.amount_cents : -g.amount_cents, accountById[g.account_id]?.currency)} {" · "}{t("dupes.count", { n: g.zeilen.length })} {" "}
{g.zeilen.map((z) => (
{z.payee || z.description || t("inbox.noText")} {t(`todo.from.${z.herkunft}`, { defaultValue: z.herkunft })} {/* Die Kennung entscheidet die Frage – deshalb steht sie da, auch wenn sie hässlich ist. */} {z.kennung || t("dupes.noRef")}
))}
))}
); }