Files
saldo/app/templates/planning/detail.html
T

853 lines
45 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Planung {{ month.label }} | {{ app_name }}{% endblock %}
{% block content %}
{% from "_ui.html" import avatar %}
<section class="planning-hero planning-hero-strong">
<div class="planning-title">
<div class="eyebrow">Planung</div>
<h1>{{ planning_heading }}</h1>
<p class="muted">Monat direkt auf dieser Seite pflegen: Einkommen, Kategorien, Einträge, Verteilung und Split-Personen.</p>
</div>
</section>
<section class="cards-grid cards-grid-four">
<article class="metric-card">
<span>Gesamteinkommen</span>
<strong>{{ summary.total_income|currency }}</strong>
</article>
<article class="metric-card">
<span>Gesamtkosten</span>
<strong>{{ summary.total_costs|currency }}</strong>
</article>
<article class="metric-card highlight">
<span>Restbetrag</span>
<strong>{{ summary.remainder|currency }}</strong>
<small>Aktuelle Verteilung {{ summary.allocation_total|currency }}</small>
</article>
<article class="metric-card soft-accent">
<span>Vorschläge</span>
<strong>{{ summary.suggestion_total|currency }}</strong>
<small>Bis Mindestziel offen</small>
</article>
</section>
<section class="planning-hub-grid">
<button type="button" class="summary-category-card utility-summary-card" data-open-dialog="income-dialog">
<div class="summary-card-head">
<strong>Einkommen</strong>
<img src="{{ url_for('static', filename='icons/pencil.svg') }}" alt="" class="ui-icon small-ui-icon">
</div>
<div class="summary-card-meta">
<span>{{ summary.total_income|currency }}</span>
<small>{{ month.incomes|length }} Zeilen</small>
</div>
</button>
<button type="button" class="summary-category-card utility-summary-card" data-open-dialog="split-people-dialog">
<div class="summary-card-head">
<strong>Personen für Splits</strong>
<img src="{{ url_for('static', filename='icons/pencil.svg') }}" alt="" class="ui-icon small-ui-icon">
</div>
<div class="summary-card-meta">
<span>{{ participants|length }}</span>
<small>{{ participant_summary.internal_count }} Nutzer, {{ participant_summary.external_count }} {{ "Gast" if participant_summary.external_count == 1 else "Gäste" }}</small>
</div>
</button>
</section>
<section class="account-board">
<article class="panel account-panel premium-panel">
<div class="panel-head account-head">
<div>
<h2>Gemeinschaftskonten</h2>
<small>{{ community_account_summary.shared_count }} Konten · {{ community_account_summary.personal_count }} privat</small>
</div>
<button class="ghost-button icon-button" type="button" data-open-dialog="community-account-create-dialog">
<img src="{{ url_for('static', filename='icons/plus.svg') }}" alt="" class="ui-icon small-ui-icon">
Konto
</button>
</div>
<div class="category-summary-grid">
{% for card in community_account_cards %}
{% if card.is_read_only %}
<div class="summary-category-card summary-static-card community-account-card">
<div class="summary-card-head">
<strong>{{ card.display_name }}</strong>
<span class="icon-label muted-label">Nur Anzeige</span>
</div>
<div class="summary-card-meta">
<span>{{ card.current_total|currency }}</span>
<small>Persönliche Auszahlung</small>
</div>
{% if card.delta %}
<div class="card-inline-note">
<span class="badge {% if card.delta > 0 %}badge-warn{% else %}badge-good{% endif %}">
{{ card.delta|currency }} zum Vormonat
</span>
</div>
{% endif %}
</div>
{% else %}
<button type="button" class="summary-category-card community-account-card" data-open-dialog="community-account-item-{{ card.community_account.id }}">
<div class="summary-card-head">
<strong>{{ card.display_name }}</strong>
<img src="{{ url_for('static', filename='icons/pencil.svg') }}" alt="" class="ui-icon small-ui-icon">
</div>
<div class="summary-card-meta">
<span>{{ card.current_total|currency }}</span>
<small>{{ card.assigned_budget_names|length }} Budgets</small>
</div>
{% if card.delta %}
<div class="card-inline-note">
<span class="badge {% if card.delta > 0 %}badge-warn{% else %}badge-good{% endif %}">
{{ card.delta|currency }} zum Vormonat
</span>
</div>
{% endif %}
{% if card.assigned_budget_names %}
<div class="card-inline-note">
<small>{{ card.assigned_budget_names|join(", ") }}</small>
</div>
{% endif %}
</button>
{% endif %}
{% endfor %}
</div>
</article>
</section>
<section class="account-board">
{% for account_data in planning_accounts %}
<article class="panel account-panel premium-panel">
<div class="panel-head account-head">
<div>
<h2>{{ account_data.account.name }}</h2>
<small>
Gesamtkosten {{ account_data.total|currency }}
{% if account_data.account.slug == "gemeinschaftskonto" %}
· Jährlich {{ (account_data.total * 12)|currency }}
{% endif %}
</small>
</div>
{% if account_data.account.slug == "sparen-und-verteilung" %}
<button class="ghost-button icon-button" type="button" data-open-dialog="dialog-add-category" data-area="distribution" data-placeholder="Name Sparkonto" data-dialog-label="Sparkonto">
<img src="{{ url_for('static', filename='icons/plus.svg') }}" alt="" class="ui-icon small-ui-icon">
Sparkonto
</button>
{% elif account_data.account.slug == "gemeinschaftskonto" %}
<button class="ghost-button icon-button" type="button" data-open-dialog="dialog-add-category" data-area="budget" data-placeholder="Name Budget">
<img src="{{ url_for('static', filename='icons/plus.svg') }}" alt="" class="ui-icon small-ui-icon">
Kategorie
</button>
{% else %}
<button class="ghost-button icon-button" type="button" data-open-dialog="dialog-add-category" data-account-id="{{ account_data.account.id }}">
<img src="{{ url_for('static', filename='icons/plus.svg') }}" alt="" class="ui-icon small-ui-icon">
Kategorie
</button>
{% endif %}
</div>
{% if account_data.categories %}
<div class="category-summary-grid">
{% for category_data in account_data.categories %}
<button type="button" class="summary-category-card {% if category_data.distribution_hint and category_data.distribution_hint.status %}range-status-{{ category_data.distribution_hint.status }}{% endif %}" data-open-dialog="{{ category_data.dialog_id }}">
<div class="summary-card-head">
<div>
<strong>{{ category_data.category.name }}</strong>
{% if category_data.category.description %}
<small>{{ category_data.category.description }}</small>
{% endif %}
</div>
<img src="{{ url_for('static', filename='icons/pencil.svg') }}" alt="" class="ui-icon small-ui-icon">
</div>
<div class="summary-card-meta">
<span>{{ category_data.total|currency }}</span>
<small>{{ category_data.entry_count }} Einträge</small>
</div>
{% if category_data.distribution_hint %}
<div class="card-inline-note">
<small>{{ category_data.distribution_hint.range_label if category_data.distribution_hint.range_label else "Rest nach Zielkonten" }}</small>
</div>
<div class="card-inline-note">
<small>Aktuell {{ category_data.distribution_hint.current_pct }} % vom Einkommen</small>
</div>
{% endif %}
{% if category_data.distribution_kind == "personal" and category_data.distribution_suggestion_total is not none %}
<div class="card-inline-note">
{% if category_data.distribution_suggestion_total > 0 %}
<span class="badge">Automatisch +{{ category_data.distribution_suggestion_total|currency }}</span>
{% elif category_data.distribution_suggestion_total < 0 %}
<span class="badge">Fehlen {{ (-category_data.distribution_suggestion_total)|currency }}</span>
{% else %}
<span class="badge">Automatisch ausgeglichen</span>
{% endif %}
</div>
{% endif %}
</button>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
{% if account_data.account.slug == "gemeinschaftskonto" %}
Noch keine Budgets angelegt. Lege die erste Budget-Kategorie direkt hier an.
{% elif account_data.account.slug == "sparen-und-verteilung" %}
Noch keine Sparkonten angelegt. Lege Sparen, Urlaub oder weitere Verteilungsziele erst bei Bedarf an.
{% else %}
In diesem Bereich gibt es noch keine Kategorien.
{% endif %}
</div>
{% endif %}
</article>
{% endfor %}
</section>
<datalist id="category-suggestions">
{% for category in categories %}
<option value="{{ category.name }}">{{ category.account.name }}</option>
{% endfor %}
</datalist>
<dialog id="income-dialog" class="app-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<div class="stack-form">
<div class="dialog-section-head">
<div>
<h3>Einkommen</h3>
<small>{{ summary.total_income|currency }} · {{ month.incomes|length }} Zeilen</small>
</div>
</div>
<div class="sheet-card-grid">
{% for income in month.incomes|sort(attribute='sort_order') %}
<form method="post" action="{{ url_for('planning.update_income', label=month.label) }}" class="sheet-card">
<input type="hidden" name="income_id" value="{{ income.id }}">
<input type="hidden" name="return_dialog" value="income-dialog">
<input name="income_label" value="{{ income.label }}" placeholder="Name der Einkommenszeile" required>
<input name="amount" type="number" step="0.01" inputmode="decimal" value="{{ income.amount }}">
<div class="dialog-action-row dialog-action-spread">
<button class="ghost-button small-button" type="submit">Speichern</button>
{% if month.incomes|length > 1 %}
<button class="ghost-button danger-button small-button" type="button" data-open-dialog="confirm-delete-income-{{ income.id }}">Löschen</button>
{% endif %}
</div>
</form>
{% if month.incomes|length > 1 %}
<dialog id="confirm-delete-income-{{ income.id }}" class="app-dialog confirm-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<div class="stack-form">
<h3>Einkommenszeile wirklich löschen?</h3>
<p class="muted">`{{ income.label }}` wird aus diesem Monat entfernt.</p>
<form method="post" action="{{ url_for('planning.delete_income', label=month.label, income_id=income.id) }}" class="dialog-action-row dialog-action-spread">
<button class="ghost-button" type="button" data-open-dialog="income-dialog">Zurück</button>
<button class="primary-button danger-fill-button" type="submit">Jetzt löschen</button>
</form>
</div>
</dialog>
{% endif %}
{% endfor %}
</div>
<form method="post" action="{{ url_for('planning.create_income', label=month.label) }}" class="soft-form-section stack-form">
<div class="dialog-section-head">
<div>
<strong>Neue Einkommenszeile</strong>
<small>Zum Beispiel Gehalt, Bonus oder Nebenjob.</small>
</div>
</div>
<input name="income_label" placeholder="Name der Einkommenszeile" required>
<input name="amount" type="number" step="0.01" inputmode="decimal" placeholder="Betrag">
<button class="primary-button" type="submit">Einkommen anlegen</button>
</form>
</div>
</dialog>
<dialog id="split-people-dialog" class="app-dialog category-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<div class="stack-form">
<div class="dialog-section-head">
<div>
<h3>Personen für Splits</h3>
<small>{{ participant_summary.internal_count }} Nutzer, {{ participant_summary.external_count }} {{ "Gast" if participant_summary.external_count == 1 else "Gäste" }}</small>
</div>
<button class="ghost-button icon-button" type="button" data-open-dialog="dialog-add-participant" data-return-dialog="split-people-dialog">
<img src="{{ url_for('static', filename='icons/plus.svg') }}" alt="" class="ui-icon small-ui-icon">
Person
</button>
</div>
<div class="participant-card-grid">
{% for participant in participants %}
{% if participant.is_app_user %}
<div class="participant-manage-card summary-static-card">
<span class="list-row-main">
{{ avatar(participant.display_name, participant.avatar_url, participant.avatar_initials, "sm") }}
<strong>{{ participant.display_name }}</strong>
</span>
<small>Nutzer · automatisch aus Benutzerkonto</small>
<span class="icon-label muted-label">Automatisch</span>
</div>
{% else %}
<button class="participant-manage-card" type="button" data-open-dialog="participant-dialog-{{ participant.id }}">
<span class="list-row-main">
{{ avatar(participant.display_name, participant.avatar_url, participant.avatar_initials, "sm") }}
<strong>{{ participant.display_name }}</strong>
</span>
<small>Gast · {{ "aktiv" if participant.is_active else "inaktiv" }}</small>
<span class="icon-label"><img src="{{ url_for('static', filename='icons/pencil.svg') }}" alt="" class="ui-icon small-ui-icon">Details</span>
</button>
{% endif %}
{% endfor %}
</div>
</div>
</dialog>
<dialog id="community-account-create-dialog" class="app-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<form method="post" action="{{ url_for('planning.create_community_account', label=month.label) }}" class="stack-form">
<div class="dialog-section-head">
<div>
<h3>Neues Gemeinschaftskonto</h3>
<small>Zum Beispiel separates Fixkosten- oder Reisekonto.</small>
</div>
</div>
<input name="name" placeholder="Kontoname" required>
<textarea name="description" rows="3" placeholder="Beschreibung optional"></textarea>
<button class="primary-button" type="submit">Konto anlegen</button>
</form>
</dialog>
{% for card in community_account_cards if not card.is_read_only %}
<dialog id="community-account-item-{{ card.community_account.id }}" class="app-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<form method="post" action="{{ url_for('planning.update_community_account', label=month.label, community_account_id=card.community_account.id) }}" class="stack-form">
<input type="hidden" name="return_dialog" value="community-account-item-{{ card.community_account.id }}">
<h3>{{ card.community_account.name }}</h3>
<small>{{ card.current_total|currency }} aktuell{% if card.delta %} · {{ card.delta|currency }} zum Vormonat{% endif %}</small>
<input name="name" value="{{ card.community_account.name }}" required>
<textarea name="description" rows="3" placeholder="Beschreibung optional">{{ card.community_account.description or '' }}</textarea>
<div class="distribution-note-card">
<div>
<strong>Budgets zuweisen</strong>
<small>Diese Budget-Kategorien laufen über dieses Gemeinschaftskonto. Bereits anders zugewiesene Budgets sind hier nicht auswählbar.</small>
</div>
</div>
<div class="participant-chip-grid">
{% for category in categories if category.account.slug == "gemeinschaftskonto" %}
{% if category.community_account_id in [none, card.community_account.id] %}
<label class="participant-chip budget-assignment-chip">
<input
type="checkbox"
name="category_ids"
value="{{ category.id }}"
{% if category.community_account_id == card.community_account.id %}checked{% endif %}>
{{ category.name }}
</label>
{% endif %}
{% endfor %}
</div>
{% if card.assigned_budget_names %}
<small>Aktuell zugewiesen: {{ card.assigned_budget_names|join(", ") }}</small>
{% endif %}
<div class="dialog-action-row dialog-action-spread">
<button class="primary-button" type="submit">Konto speichern</button>
<button class="ghost-button danger-button" type="button" data-open-dialog="confirm-delete-community-account-{{ card.community_account.id }}">Konto löschen</button>
</div>
</form>
</dialog>
<dialog id="confirm-delete-community-account-{{ card.community_account.id }}" class="app-dialog confirm-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<div class="stack-form">
<h3>Konto wirklich löschen?</h3>
<p class="muted">`{{ card.community_account.name }}` wird ausgeblendet und seine Budget-Zuordnungen werden gelöst.</p>
<form method="post" action="{{ url_for('planning.delete_community_account', label=month.label, community_account_id=card.community_account.id) }}" class="dialog-action-row dialog-action-spread">
<button class="ghost-button" type="button" data-open-dialog="community-account-item-{{ card.community_account.id }}">Zurück</button>
<button class="primary-button danger-fill-button" type="submit">Jetzt löschen</button>
</form>
</div>
</dialog>
{% endfor %}
<dialog id="dialog-add-category" class="app-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<form method="post" action="{{ url_for('planning.create_category', label=month.label) }}" class="stack-form">
<input type="hidden" name="area" value="budget" data-dialog-area>
<input type="hidden" name="account_id" value="" data-dialog-account-id>
<h3>Neue Kategorie</h3>
<select name="community_account_id" data-community-account-field>
<option value="">Gemeinschaftskonto zuweisen</option>
{% for community_account in community_accounts if community_account.account_type == "shared" %}
<option value="{{ community_account.id }}">{{ community_account.name }}</option>
{% endfor %}
</select>
<input name="name" list="category-suggestions" placeholder="Name Budget" data-dialog-name-placeholder required>
<button class="primary-button" type="submit">Kategorie anlegen</button>
</form>
</dialog>
<dialog id="dialog-add-entry" class="app-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<form method="post" action="{{ url_for('planning.create_entry', label=month.label) }}" class="stack-form">
<h3>Neuen Eintrag anlegen</h3>
<input type="hidden" name="area" value="budget" data-dialog-area>
<input type="hidden" name="account_id" value="" data-dialog-account-id>
<input type="hidden" name="return_dialog" value="" data-dialog-return-dialog>
<input name="category_name" list="category-suggestions" data-dialog-category-name placeholder="Kategorie" required>
<input name="name" placeholder="Eintragsname" required>
<div class="sheet-card-grid" data-annual-sync-wrapper>
<label>
Monatlich
<input name="planned_amount" type="number" step="0.01" inputmode="decimal" placeholder="Monatlicher Betrag" data-annual-sync="monthly">
</label>
<label data-annual-visibility>
Jährlich
<input name="annual_amount" type="number" step="0.01" inputmode="decimal" placeholder="Jährlicher Betrag" data-annual-sync="yearly">
</label>
</div>
<select name="benefit_scope">
{% for option in benefit_options %}
<option value="{{ option.value }}">Betrifft {{ option.label }}</option>
{% endfor %}
</select>
<label class="check-label">
<input type="checkbox" name="is_allocation_target">
Sparkonto
<small>Zeigt Zielbereich und direkte Budgetpflege über die Karte in Planung.</small>
</label>
<textarea name="note" rows="3" placeholder="Notiz optional"></textarea>
<details class="split-picker">
<summary class="ghost-button">Mit anderen Personen teilen</summary>
<div class="participant-chip-grid split-panel">
{% for participant in participants %}
<label class="participant-chip"><input type="checkbox" name="participant_ids" value="{{ participant.id }}"> {{ participant.display_name }}</label>
{% endfor %}
</div>
</details>
<button class="primary-button" type="submit">Eintrag anlegen</button>
</form>
</dialog>
<dialog id="dialog-add-participant" class="app-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<form method="post" action="{{ url_for('planning.create_participant', label=month.label) }}" class="stack-form" enctype="multipart/form-data">
<input type="hidden" name="return_dialog" value="" data-dialog-return-dialog>
<h3>Neue Person</h3>
<input name="name" placeholder="Name" required>
<label>
<span>Avatar hochladen</span>
<input name="avatar_file" type="file" accept="image/*">
</label>
<input name="avatar_url" placeholder="Avatar-URL optional">
<label class="check-label"><input type="checkbox" name="is_external" checked> Extern ohne App-Zugang</label>
<button class="primary-button" type="submit">Person anlegen</button>
</form>
</dialog>
{% for account_data in planning_accounts %}
{% for category_data in account_data.categories %}
{% if not category_data.is_personal_split %}
<dialog id="{{ category_data.dialog_id }}" class="app-dialog category-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<div class="stack-form">
<div class="dialog-section-head">
<div>
<h3>{{ category_data.category.name }}</h3>
<small>{{ category_data.total|currency }} · {{ category_data.entry_count }} Einträge</small>
</div>
{% if category_data.allow_new_entries %}
<button class="ghost-button icon-button" type="button" data-open-dialog="dialog-add-entry" data-account-id="{{ category_data.category.account_id }}" data-category-name="{{ category_data.category.name }}" data-return-dialog="{{ category_data.dialog_id }}" data-area="{{ 'budget' if category_data.category.account.slug == 'gemeinschaftskonto' else 'distribution' }}">
<img src="{{ url_for('static', filename='icons/plus.svg') }}" alt="" class="ui-icon small-ui-icon">
Eintrag
</button>
{% endif %}
</div>
{% if category_data.distribution_kind == "single" %}
{% set distribution_entry = category_data.entries|first %}
{% set distribution_suggestion = distribution_entry.distribution_suggestion if distribution_entry else none %}
{% if category_data.direct_entry %}
<form method="post" action="{{ url_for('planning.update_entry', label=month.label) }}" class="soft-form-section stack-form">
<input type="hidden" name="value_id" value="{{ category_data.direct_entry.value.id }}">
<input type="hidden" name="return_dialog" value="{{ category_data.dialog_id }}">
<input type="hidden" name="entry_name" value="{{ category_data.direct_entry.entry.name }}">
<input type="hidden" name="category_id" value="{{ category_data.direct_entry.entry.category_id }}">
<input type="hidden" name="benefit_scope" value="{{ category_data.direct_entry.entry.benefit_scope }}">
<div class="dialog-section-head">
<div>
<strong>Budget direkt anpassen</strong>
<small>Wenn `Sparkonto` aktiv ist, steuert dieser Eintrag das Budget direkt auf der Karte.</small>
</div>
</div>
<label class="check-label">
<input
type="checkbox"
name="is_allocation_target"
{% if category_data.direct_entry.entry.is_allocation_target %}checked{% endif %}>
Sparkonto
<small>Aktiviert Zielbereich und direkte Budgetpflege über diese Karte.</small>
</label>
<label>
Monatliches Budget
<input
name="planned_amount"
type="number"
step="0.01"
inputmode="decimal"
value="{{ category_data.direct_entry.value.planned_amount }}">
</label>
<label class="check-label">
<input
type="checkbox"
name="allocation_is_locked"
{% if category_data.direct_entry.distribution_allocation and category_data.direct_entry.distribution_allocation.is_locked %}checked{% endif %}>
Budget fixieren
</label>
<button class="primary-button" type="submit">Budget speichern</button>
</form>
{% endif %}
{% if category_data.direct_entry and category_data.direct_entry.entry.is_allocation_target %}
<div class="distribution-note-card">
<div>
<strong>Verteilung</strong>
<small>
Der Betrag in dieser Kategorie steuert direkt die monatliche Verteilung.
{% if category_data.distribution_hint %}
Zielbereich {{ category_data.distribution_hint.range_label }} vom Einkommen.
{% endif %}
</small>
</div>
{% if distribution_suggestion %}
<div class="row-actions">
<span class="badge">Noch offen {{ category_data.distribution_suggestion_total|currency }}</span>
<form method="post" action="{{ url_for('planning.accept_single_suggestion', label=month.label, account_id=distribution_suggestion.target_account_id) }}">
<input type="hidden" name="return_dialog" value="{{ category_data.dialog_id }}">
<button class="ghost-button small-button" type="submit">Übernehmen</button>
</form>
</div>
{% endif %}
</div>
{% if category_data.distribution_hint %}
<form method="post" action="{{ url_for('planning.update_distribution_settings', label=month.label, slug=category_data.distribution_account_slug) }}" class="soft-form-section stack-form">
<input type="hidden" name="return_dialog" value="{{ category_data.dialog_id }}">
<div class="dialog-section-head">
<div>
<strong>Zielbereich anpassen</strong>
<small>Der Vorschlag arbeitet innerhalb dieses Prozentbereichs.</small>
</div>
</div>
<div class="sheet-card-grid">
<label>
Von %
<input type="number" name="min_pct" min="0" max="100" step="1" value="{{ category_data.distribution_hint.min_pct }}">
</label>
<label>
Bis %
<input type="number" name="max_pct" min="0" max="100" step="1" value="{{ category_data.distribution_hint.max_pct }}">
</label>
</div>
<button class="ghost-button small-button" type="submit">Bereich speichern</button>
</form>
{% endif %}
{% endif %}
{% endif %}
{% if category_data.distribution_kind != "single" %}
<form method="post" action="{{ url_for('planning.update_category', label=month.label, category_id=category_data.category.id) }}" class="stack-form soft-form-section">
<input name="name" value="{{ category_data.category.name }}" required>
{% if category_data.category.account.slug == "gemeinschaftskonto" %}
<select name="community_account_id">
<option value="">Kein Gemeinschaftskonto</option>
{% for community_account in community_accounts if community_account.account_type == "shared" %}
<option value="{{ community_account.id }}" {% if category_data.category.community_account_id == community_account.id %}selected{% endif %}>{{ community_account.name }}</option>
{% endfor %}
</select>
{% endif %}
<textarea name="description" rows="3" placeholder="Beschreibung optional">{{ category_data.category.description or '' }}</textarea>
<div class="dialog-action-row">
<button class="primary-button" type="submit">Kategorie speichern</button>
</div>
</form>
{% endif %}
<div class="dialog-entry-list">
{% for item in category_data.entries %}
{% if not (category_data.direct_entry and category_data.direct_entry.value.id == item.value.id) %}
<button type="button" class="dialog-entry-row" data-open-dialog="{{ item.dialog_id }}">
<div>
<strong>{{ item.entry.name }}</strong>
<small>
{{ item.benefit_label }}
{% if item.share_names %} · Split: {{ item.share_names }}{% endif %}
{% if item.value.note %} · {{ item.value.note }}{% endif %}
</small>
</div>
<div class="entry-row-trailing">
{% if item.entry.category and item.entry.category.account and item.entry.category.account.slug == "gemeinschaftskonto" and item.benefit_users %}
<div class="stacked-avatars">
{% for user in item.benefit_users %}
{{ avatar(user.ui_name, user.avatar_url, user.avatar_initials, "sm", "stacked-avatar") }}
{% endfor %}
</div>
{% endif %}
<span>{{ item.amount|currency }}</span>
</div>
</button>
{% endif %}
{% endfor %}
</div>
<button class="ghost-button danger-button" type="button" data-open-dialog="confirm-delete-category-{{ category_data.category.id }}">Kategorie löschen</button>
</div>
</dialog>
<dialog id="confirm-delete-category-{{ category_data.category.id }}" class="app-dialog confirm-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<div class="stack-form">
<h3>Kategorie wirklich löschen?</h3>
<p class="muted">`{{ category_data.category.name }}` wird ausgeblendet und ihre Einträge werden mit ausgeblendet.</p>
<form method="post" action="{{ url_for('planning.delete_category', label=month.label, category_id=category_data.category.id) }}" class="dialog-action-row dialog-action-spread">
<button class="ghost-button" type="button" data-open-dialog="{{ category_data.dialog_id }}">Zurück</button>
<button class="primary-button danger-fill-button" type="submit">Jetzt löschen</button>
</form>
</div>
</dialog>
{% endif %}
{% endfor %}
{% endfor %}
{% for account_data in planning_accounts %}
{% for category_data in account_data.categories %}
{% if category_data.is_personal_split %}
<dialog id="{{ category_data.dialog_id }}" class="app-dialog category-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<div class="stack-form">
<div class="dialog-section-head">
<div>
<h3>Persönliche Auszahlung</h3>
<small>{{ category_data.total|currency }} · {{ category_data.entry_count }} Einträge</small>
</div>
</div>
<div class="distribution-note-card">
<div>
<strong>Split</strong>
<small>Erst werden Sparen, Urlaub und Freizeit bedient. Der verbleibende Rest wird danach automatisch auf die persönliche Auszahlung verteilt.</small>
</div>
{% if category_data.distribution_suggestion_total > 0 %}
<span class="badge">Automatisch +{{ category_data.distribution_suggestion_total|currency }}</span>
{% elif category_data.distribution_suggestion_total < 0 %}
<span class="badge">Fehlen {{ (-category_data.distribution_suggestion_total)|currency }}</span>
{% else %}
<span class="badge">Automatisch ausgeglichen</span>
{% endif %}
</div>
<form method="post" action="{{ url_for('planning.update_personal_split', label=month.label) }}" class="soft-form-section stack-form">
<input type="hidden" name="return_dialog" value="{{ category_data.dialog_id }}">
<div class="dialog-section-head">
<div>
<strong>Aufteilung anpassen</strong>
<small>Wenn du einen Wert aenderst, wird der andere automatisch auf 100 % ergaenzt.</small>
</div>
</div>
<div class="sheet-card-grid">
<label>
{{ category_data.distribution_items[0].label if category_data.distribution_items|length > 0 else 'Person 1' }} in %
<input
type="number"
name="flo_pct"
min="0"
max="100"
step="1"
value="{{ personal_split.flo_pct }}"
data-personal-split="flo">
</label>
<label>
{{ category_data.distribution_items[1].label if category_data.distribution_items|length > 1 else 'Person 2' }} in %
<input
type="number"
name="desi_pct"
min="0"
max="100"
step="1"
value="{{ personal_split.desi_pct }}"
data-personal-split="desi">
</label>
</div>
<button class="ghost-button small-button" type="submit">Split speichern</button>
</form>
<div class="personal-split-grid">
{% for distribution_item in category_data.distribution_items %}
<div class="sheet-card compact-sheet-card">
<strong>{{ distribution_item.label }}</strong>
<span>{{ distribution_item.auto_amount|currency }}</span>
<small>Automatisch aus Restbetrag berechnet</small>
</div>
{% endfor %}
</div>
<div class="dialog-entry-list">
{% for item in category_data.entries %}
<div class="dialog-entry-row static-entry-row">
<div>
<strong>{{ item.entry.name }}</strong>
<small>
{{ item.benefit_label }}
{% if item.value.note %} · {{ item.value.note }}{% endif %}
</small>
</div>
<span>{{ item.amount|currency }}</span>
</div>
{% endfor %}
</div>
</div>
</dialog>
{% endif %}
{% endfor %}
{% endfor %}
{% for account_data in planning_accounts %}
{% for category_data in account_data.categories %}
{% for item in category_data.entries %}
<dialog id="{{ item.dialog_id }}" class="app-dialog entry-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<form method="post" action="{{ url_for('planning.update_entry', label=month.label) }}" class="stack-form">
<input type="hidden" name="value_id" value="{{ item.value.id }}">
<input type="hidden" name="return_dialog" value="{{ category_data.dialog_id }}">
<div class="dialog-section-head">
<h3>{{ item.entry.name }}</h3>
</div>
<input name="entry_name" value="{{ item.entry.name }}" required>
<select name="category_id">
{% for category in categories %}
<option value="{{ category.id }}" {% if item.entry.category_id == category.id %}selected{% endif %}>{{ category.name }}</option>
{% endfor %}
</select>
{% if item.entry.category.account.slug == "gemeinschaftskonto" %}
<div class="sheet-card-grid" data-annual-sync-wrapper>
<label>
Monatlich
<input name="planned_amount" type="number" step="0.01" inputmode="decimal" value="{{ item.value.planned_amount }}" data-annual-sync="monthly">
</label>
<label>
Jährlich
<input name="annual_amount" type="number" step="0.01" inputmode="decimal" value="{{ '%.2f'|format(item.value.planned_amount * 12) }}" data-annual-sync="yearly">
</label>
</div>
{% else %}
<input name="planned_amount" type="number" step="0.01" inputmode="decimal" value="{{ item.value.planned_amount }}">
{% endif %}
<select name="benefit_scope">
{% for option in benefit_options %}
<option value="{{ option.value }}" {% if item.entry.benefit_scope == option.value %}selected{% endif %}>Betrifft {{ option.label }}</option>
{% endfor %}
</select>
<label class="check-label">
<input type="checkbox" name="is_allocation_target" {% if item.entry.is_allocation_target %}checked{% endif %}>
Sparkonto
<small>Zeigt Zielbereich und direkte Budgetpflege über die Karte in Planung.</small>
</label>
<textarea name="note" rows="4" placeholder="Notiz">{{ item.value.note or '' }}</textarea>
{% if item.is_distribution_entry and item.distribution_allocation %}
<div class="distribution-note-card">
<div>
<strong>Verteilung für {{ item.entry.name }}</strong>
<small>
Dieser Eintrag steuert den Zielbetrag in der Verteilung.
{% if item.distribution_hint %}
Zielbereich {{ item.distribution_hint.range_label }} vom Einkommen.
{% endif %}
</small>
</div>
{% if item.distribution_suggestion %}
<span class="badge">Noch offen {{ item.distribution_hint.remaining_amount if item.distribution_hint else item.distribution_suggestion.suggested_amount|currency }}</span>
{% endif %}
</div>
<label class="check-label"><input type="checkbox" name="allocation_is_locked" {% if item.distribution_allocation.is_locked %}checked{% endif %}> Verteilung fixieren</label>
{% endif %}
<details class="split-picker" {% if item.entry.share_rules %}open{% endif %}>
<summary class="ghost-button">Mit anderen Personen teilen</summary>
<div class="participant-chip-grid split-panel">
{% for participant in participants %}
<label class="participant-chip">
<input
type="checkbox"
name="participant_ids"
value="{{ participant.id }}"
{% if item.entry.share_rules|selectattr('participant_id', 'equalto', participant.id)|list %}checked{% endif %}>
{{ participant.display_name }}
</label>
{% endfor %}
</div>
</details>
<div class="dialog-action-row dialog-action-spread">
<button class="primary-button" type="submit">Speichern</button>
<button class="ghost-button danger-button" type="button" data-open-dialog="confirm-delete-entry-{{ item.value.id }}">Eintrag löschen</button>
</div>
</form>
</dialog>
<dialog id="confirm-delete-entry-{{ item.value.id }}" class="app-dialog confirm-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<div class="stack-form">
<h3>Eintrag wirklich löschen?</h3>
<p class="muted">`{{ item.entry.name }}` wird ausgeblendet und erscheint nicht mehr in der Planung.</p>
<form method="post" action="{{ url_for('planning.delete_entry', label=month.label, entry_id=item.entry.id) }}" class="dialog-action-row dialog-action-spread">
<input type="hidden" name="return_dialog" value="{{ category_data.dialog_id }}">
<button class="ghost-button" type="button" data-open-dialog="{{ item.dialog_id }}">Zurück</button>
<button class="primary-button danger-fill-button" type="submit">Jetzt löschen</button>
</form>
</div>
</dialog>
{% endfor %}
{% endfor %}
{% endfor %}
{% for participant in participants if not participant.is_app_user %}
<dialog id="participant-dialog-{{ participant.id }}" class="app-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<form method="post" action="{{ url_for('planning.update_participant', label=month.label, participant_id=participant.id) }}" class="stack-form" enctype="multipart/form-data">
<input type="hidden" name="return_dialog" value="split-people-dialog">
<h3>Person bearbeiten</h3>
<input name="name" value="{{ participant.display_name }}" required>
<label>
<span>Avatar hochladen</span>
<input name="avatar_file" type="file" accept="image/*">
</label>
<input name="avatar_url" value="{{ participant.avatar_url or '' }}" placeholder="Avatar-URL optional">
<label class="check-label"><input type="checkbox" name="is_external" {% if participant.is_external %}checked{% endif %}> Extern ohne App-Zugang</label>
<label class="check-label"><input type="checkbox" name="is_active" {% if participant.is_active %}checked{% endif %}> Aktiv</label>
<button class="primary-button" type="submit">Speichern</button>
</form>
</dialog>
{% endfor %}
{% endblock %}