feat: Phase 1 – Konten, Kategorien, Transaktionen, EZB-Währungsumrechnung
Backend: - Modelle + Migration 0002: accounts, account_identifiers, category_groups, categories, transactions, exchange_rates (alle owner-gescoped). - EZB-Kursdienst (90-Tage/Historie) + Backfill-Skript; Umrechnung EUR<->CHF zum Buchungsdatum mit lokalem Cache und manueller Übersteuerung; EUR-Pivot (amount_eur_cents) eingefroren. - Kontostände + Netto-Vermögen (Umrechnung in Hauptwährung). - Router: accounts (inkl. IBAN-Identifikatoren), categories (Gruppen+Kategorien), transactions (Ausgabe/Einnahme/Transfer, cross-currency), rates. - Gruppierte Standard-Kategorien je Nutzer beim Anlegen. - pytest-Suite: Währung, Salden, Cross-Currency-Transfer, Nutzer-Isolation. Frontend: - Seiten Konten, Kategorien, Buchungen; Icon-Chip, Geldbetrag mit Umrechnung, Icon-/Farbwähler, Modal; erweitertes SVG-Icon-Set (Konto-/Kategorie-Icons). - Währungs-/Datumsformatierung (EUR de-DE, CHF de-CH), i18n DE/EN erweitert. - Live-Umrechnungs-Vorschau im Transfer-Formular. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -31,3 +31,26 @@ export function formatDate(iso, locale = "de") {
|
||||
if (Number.isNaN(d.getTime())) return String(iso);
|
||||
return new Intl.DateTimeFormat(locale === "en" ? "en-GB" : "de-DE").format(d);
|
||||
}
|
||||
|
||||
/** Eingabetext ("12,34" oder "12.34") in Ganzzahl-Cent; null bei leer/ungültig. */
|
||||
export function parseAmountToCents(str) {
|
||||
if (str == null) return null;
|
||||
const cleaned = String(str).trim().replace(/\s/g, "").replace(",", ".");
|
||||
if (cleaned === "") return null;
|
||||
const value = Number.parseFloat(cleaned);
|
||||
if (Number.isNaN(value)) return null;
|
||||
return Math.round(value * 100);
|
||||
}
|
||||
|
||||
/** Cent-Betrag als bearbeitbarer Eingabetext ("1234" -> "12,34"). */
|
||||
export function centsToInput(cents) {
|
||||
if (cents == null) return "";
|
||||
return (cents / 100).toFixed(2).replace(".", ",");
|
||||
}
|
||||
|
||||
/** Heutiges Datum als YYYY-MM-DD (lokal). */
|
||||
export function todayIso() {
|
||||
const d = new Date();
|
||||
const z = (n) => String(n).padStart(2, "0");
|
||||
return `${d.getFullYear()}-${z(d.getMonth() + 1)}-${z(d.getDate())}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user