feat: Eine Ausgabe, die kein Verlust ist

Sparen zaehlt im Budget wie eine Ausgabe – zu Recht, das Geld ist diesen Monat
gebunden. Es stand danach aber in Rot da, und wer tausend zuruecklegte statt
achthundert, bekam eine Warnung fuer etwas Gutes.

Ein Haken an der Kategorie stellt das ab. Zwei Folgen:

Der Betrag steht neutral. NICHT gruen: Gruen heisst in Certain ueberall "Geld
kommt rein", und CHF 1'010 in Gruen liest sich als Einnahme. Grau sagt genau
das Richtige – kein Verlust, kein Gewinn.

Und mehr als geplant ist keine Ueberschreitung mehr. Das gilt auch eine Ebene
hoeher: Eine Gruppe misst sich an dem, was WIRKLICH zu viel war. Sonst faerbt
sich "Finanzen" rot, weil jemand gespart hat, und die echte Ueberschreitung
daneben faellt nicht mehr auf. Angezeigt werden weiter die vollen Summen; nur
das Urteil laesst das Sparen aus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-07 15:28:36 +02:00
co-authored by Claude Opus 5
parent 15c2a164ec
commit ff621984dc
12 changed files with 210 additions and 21 deletions
+12 -3
View File
@@ -197,8 +197,10 @@ export default function Categories() {
groupId,
form: editing
? { name: editing.name, icon: editing.icon, color: editing.color,
excluded_from_stats: !!editing.excluded_from_stats }
: { name: "", icon: "more-horizontal", color: "#6b7480", excluded_from_stats: false },
excluded_from_stats: !!editing.excluded_from_stats,
spending_is_good: !!editing.spending_is_good }
: { name: "", icon: "more-horizontal", color: "#6b7480",
excluded_from_stats: false, spending_is_good: false },
});
}
@@ -211,7 +213,8 @@ export default function Categories() {
else await api.createGroup({ name: form.name.trim(), kind: form.kind, icon: form.icon, color: form.color });
} else {
const rumpf = { name: form.name.trim(), icon: form.icon, color: form.color,
excluded_from_stats: !!form.excluded_from_stats };
excluded_from_stats: !!form.excluded_from_stats,
spending_is_good: !!form.spending_is_good };
if (editing) await api.updateCategory(editing.id, rumpf);
else await api.createCategory({ group_id: groupId, ...rumpf });
}
@@ -339,6 +342,12 @@ export default function Categories() {
<span>{t("categories.excludedFromStats")}</span>
</label>
<p className="muted small" style={{ marginTop: -6 }}>{t("categories.excludedHint")}</p>
<label className="checkline">
<input type="checkbox" checked={!!modal.form.spending_is_good}
onChange={(e) => setModal({ ...modal, form: { ...modal.form, spending_is_good: e.target.checked } })} />
<span>{t("categories.spendingIsGood")}</span>
</label>
<p className="muted small" style={{ marginTop: -6 }}>{t("categories.spendingIsGoodHint")}</p>
</>
)}
<label>