@@ -297,102 +297,138 @@
{% endfor %}
< / details >
< details >
< summary > Modifikatoren< / summary >
< table >
< thead >
< tr > < th > Ziel< / th > < th > Art< / th > < th > Wert< / th > < th > < / th > < / tr >
< / thead >
< tbody >
{% for m in row.modifiers %}
< tr >
< td >
{% if m.target_type == "category" %}Kategorie: {{ category _names.get(m.target_id, m.target_id) }}
{% else %}Posten: {{ recurring_names.get(m.target_id, m.target_id) }}{% endif %}
< / td >
< td > {{ m.kind|de_label } }< / td >
< td >
{% if m.kind == 'ende' %}{{ m.end_date.strftime('%d.%m.%Y') if m.end_date else '– ' }}
{% else %}{{ m.value|eur }}{% endif %}
< / td >
< td >
< form class = "inline-form" hx-delete = "/api/scenarios/{{ sc.id }}/modifiers/{{ m.id }}" hx-swap = "none"
hx-on::after-request = "if(event.detail.successful){window.location.reload()}" >
< button type = "submit" > Löschen < / button >
< / form >
< / td >
< / tr >
{% else %}
< tr > < td colspan = "4" > Keine Modifikatoren.< / td > < / tr >
{% endfor %}
< / tbody >
< / table >
< h3 > Einträge< / h3 >
< table >
< thead >
< tr > < th > Was< / th > < th > Art< / th > < th > Wert/Betrag< / th > < th > Datum< / th > < th > < / th > < / tr >
< / thead >
< tbody >
{% for m in row.modifiers %}
< tr id = "mod-row-{{ m.id }}" >
< td >
{% if m.target_type == "category" %}Kategorie: {{ category_names.get(m.target_id, m.target_id) }}
{% else %}Posten: {{ recurring _names.get(m.target_id, m.target_id) }}{% endif %}
< / td >
< td > {{ m.kind|de_label }}< / td >
< td > {% if m.kind in ('percent', 'absolute') %}{{ m.value|eur }}{% else %}– {% endif % }< / td >
< td > {% if m.kind == 'ende' %}{{ m.end_date.strftime('%d.%m.%Y') if m.end_date else '– ' }}{% else %}– {% endif %}< / td >
< td >
< button type = "button" onclick = "toggleEdit('mod', {{ m.id }}, true)" > Bearbeiten< / button >
< form class = "inline-form" hx-delete = "/api/scenarios/{{ sc.id }}/modifiers/{{ m.id }}" hx-swap = "none"
hx-on::after-request = "if(event.detail.successful){window.location.reload()}" >
< button type = "submit" > Löschen< / button >
< / form >
< / td >
< / tr >
< tr id = "mod-edit-{{ m.id }}" hidden >
< td colspan = "5" >
< form hx-ext = "json-form" hx-patch = "/api/scenarios/{{ sc.id }}/modifiers/{{ m.id }}" hx-swap = "none"
hx-on::after-request = "if(event.detail.successful){window.location.reload()}" >
< label > Ziel-Typ
< select name = "target_type" data-type = "str" onchange = "onModTargetTypeChange(this)" >
< option value = "category" { % if m . target_type = = ' category ' % } selected { % endif % } > Kategorie< / option >
< option value = "recurring" { % if m . target_type = = ' recurring ' % } selected { % endif % } > Wiederkehrender Posten< / option >
< / select >
< / label >
< span class = "mod-target-category" { % if m . target_type ! = ' category ' % } style = "display:none" { % endif % } >
< label > Kategorie
< select name = "target_id" data-type = "int" { % if m . target_type ! = ' category ' % } disabled { % endif % } >
{% for c in categories %}< option value = "{{ c.id }}" { % if m . target_type = = ' category ' and c . id = = m . target_id % } selected { % endif % } > {{ c.name }}< / option > {% endfor %}
< / select >
< / label >
< / span >
< span class = "mod-target-recurring" { % if m . target_type ! = ' recurring ' % } style = "display:none" { % endif % } >
< label > Posten
< select name = "target_id" data-type = "int" { % if m . target_type ! = ' recurring ' % } disabled { % endif % } >
{% for r in recurring %}< option value = "{{ r.id }}" { % if m . target_type = = ' recurring ' and r . id = = m . target_id % } selected { % endif % } > {{ r.name }}< / option > {% endfor %}
< / select >
< / label >
< / span >
< label > Art
< select name = "kind" data-type = "str" onchange = "onEntryArtChange(this)" >
{% for k in modifier_kinds %}< option value = "{{ k }}" { % if k = = m . kind % } selected { % endif % } > {{ k|de_label }}< / option > {% endfor %}
< / select >
< / label >
< label > Wert < input type = "text" name = "value" data-type = "amount" value = "{{ m.value|eur }}" { % if m . kind not in ( ' percent ' , ' absolute ' ) % } disabled { % endif % } > < / label >
< label > Endet am < input type = "date" name = "end_date" value = "{{ m.end_date.isoformat() if m.end_date else '' }}" { % if m . kind ! = ' ende ' % } disabled { % endif % } > < / label >
< button type = "submit" > Speichern< / button >
< button type = "button" onclick = "toggleEdit('mod', {{ m.id }}, false)" > Abbrechen< / button >
< / form >
< / td >
< / tr >
{% endfor %}
{% for sp in row.planned_items %}
< tr id = "spi-row-{{ sp.id }}" >
< td > {{ sp.name }}< / td >
< td > Einmalzahlung< / td >
< td class = "{{ 'neg' if sp.amount < 0 else '' }}" > {{ sp.amount|eur }} €< / td >
< td > {{ sp.due.strftime('%d.%m.%Y') }}< / td >
< td >
< button type = "button" onclick = "toggleEdit('spi', {{ sp.id }}, true)" > Bearbeiten< / button >
< form class = "inline-form" hx-delete = "/api/scenarios/{{ sc.id }}/planned/{{ sp.id }}" hx-swap = "none"
hx-confirm = "„{{ sp.name }}“ wirklich löschen?"
hx-on::after-request = "if(event.detail.successful){window.location.reload()}" >
< button type = "submit" > Löschen< / button >
< / form >
< / td >
< / tr >
< tr id = "spi-edit-{{ sp.id }}" hidden >
< td colspan = "5" >
< form hx-ext = "json-form" hx-patch = "/api/scenarios/{{ sc.id }}/planned/{{ sp.id }}" hx-swap = "none"
hx-on::after-request = "if(event.detail.successful){window.location.reload()}" >
< label > Name < input type = "text" name = "name" value = "{{ sp.name }}" required > < / label >
< label > Betrag < input type = "text" name = "amount" data-type = "amount" value = "{{ sp.amount|eur }}" required > < / label >
< label > Fällig am < input type = "date" name = "due" value = "{{ sp.due.isoformat() }}" required > < / label >
< button type = "submit" > Speichern< / button >
< button type = "button" onclick = "toggleEdit('spi', {{ sp.id }}, false)" > Abbrechen< / button >
< / form >
< / td >
< / tr >
{% endfor %}
{% if not row.modifiers and not row.planned_items %}
< tr > < td colspan = "5" > Keine Einträge.< / td > < / tr >
{% endif %}
< / tbody >
< / table >
< form hx-ext = "json-form" hx-post = "/api/scenarios/{{ sc.id }}/modifiers" hx-swap = "none"
hx-on::after-request = "if(event.detail.successful){window.location.reload()}" >
< label > Ziel-Typ
< select name = "target_type" data-type = "str" onchange = "onModTargetTypeChange(this) ">
< option value = "category" > Kategorie< / opti on>
< option value = "recurring" > Wiederkehrender Posten< / option >
< form hx-ext = "json-form" hx-post = "/api/scenarios/{{ sc.id }}/modifiers" hx-swap = "none"
data-modifiers-url = "/api/scenarios/{{ sc.id }}/modifiers"
data-planned-url = "/api/scenarios/{{ sc.id }}/planned"
hx-on::after-request = "if(event.detail.successful){window.location.reload()} ">
< strong > Neuer Eintrag:< / str ong >
< label > Eintragsart
< select name = "kind" data-type = "str" onchange = "onEntryArtChange(this)" >
{% for k in modifier_kinds %}< option value = "{{ k }}" > {{ k|de_label }}< / option > {% endfor %}
< option value = "einmal" > Einmalzahlung< / option >
< / select >
< / label >
< label > Ziel-Typ
< select name = "target_type" data-type = "str" onchange = "onModTargetTypeChange(this)" >
< option value = "category" > Kategorie< / option >
< option value = "recurring" > Wiederkehrender Posten< / option >
< / select >
< / label >
< span class = "mod-target-category" >
< label > Kategorie
< select name = "target_id" data-type = "int" >
{% for c in categories %}< option value = "{{ c.id }}" > {{ c.name }}< / option > {% endfor %}
< / select >
< / label >
< span class = "mod-target-category" >
< label > Kategorie
< select name = "target_id" data-type = "int" >
{% for c in categories %}< option value = "{{ c.id }}" > {{ c.name }}< / option > {% endfor %}
< / select >
< / label >
< / span >
< span class = "mod-target-recurring" style = "display:none" >
< label > Posten
< select name = "target_id" data-type = "int" disabled >
{% for r in recurring %}< option value = "{{ r.id }}" > {{ r.name }}< / option > {% endfor %}
< / select >
< / label >
< / span >
< label > Art
< select name = "kind" data-type = "str" onchange = "onModKindChange(this)" >
{% for k in modifier_kinds %}< option value = "{{ k }}" > {{ k|de_label }}< / option > {% endfor %}
</ span >
< span class = "mod-target-recurring" style = "display:none" >
< label > Posten
< select name = "target_id" data-type = "int" disabled >
{% for r in recurring %}< option value = "{{ r.id }}" > {{ r.name }}< / option > {% endfor %}
< / select >
< / label >
< label > Wert < input type = "text" name = "value" data-type = "amount" value = "0" > < / label >
< label > Endet am < input type = "da te" name = "end_date" disabled > < / label >
< button type = "submit" > Modifikator hinzufügen < / button >
< / form >
< / details >
< details >
< summary > Einmalzahlungen in diesem Szenario< / summary >
< table >
< thead > < tr > < th > Name< / th > < th > Betrag< / th > < th > Fällig am< / th > < th > < / th > < / tr > < / thead >
< tbody >
{% for sp in row.planned_items %}
< tr >
< td > {{ sp.name }}< / td >
< td class = "{{ 'neg' if sp.amount < 0 else '' }}" > {{ sp.amount|eur }} €< / td >
< td > {{ sp.due.strftime('%d.%m.%Y') }}< / td >
< td >
< form class = "inline-form" hx-delete = "/api/scenarios/{{ sc.id }}/planned/{{ sp.id }}" hx-swap = "none"
hx-confirm = "„{{ sp.name }}“ wirklich löschen?"
hx-on::after-request = "if(event.detail.successful){window.location.reload()}" >
< button type = "submit" > Löschen< / button >
< / form >
< / td >
< / tr >
{% else %}
< tr > < td colspan = "4" > Keine szenario-eigenen Einmalzahlungen.< / td > < / tr >
{% endfor %}
< / tbody >
< / table >
< form hx-ext = "json-form" hx-post = "/api/scenarios/{{ sc.id }}/planned" hx-swap = "none"
hx-on::after-request = "if(event.detail.successful){window.location.reload()}" >
< label > Name < input type = "text" name = "name" required > < / label >
< label > Betrag < input type = "text" name = "amount" data-type = "amount" placeholder = "5000,00" required > < / label >
< label > Fällig am < input type = "date" name = "due" required > < / label >
< button type = "submit" > Einmalzahlung hinzufügen< / button >
< / form >
< p class = "hint" > Zählt nur in diesem Szenario — unabhängig von „Einmalposten einschließen“.< / p >
< / details >
< / span >
< label > Wert < input type = "text " name = "value" data-type = "amount" value = "0" > < / label >
< label > Endet am < input type = "date" name = "end_date" disabled > < / label >
< label > Name < input type = "text" name = "name" disabled > < / label >
< label > Betrag < input type = "text" name = "amount" data-type = "amount" placeholder = "5000,00" disabled > < / label >
< label > Fällig am < input type = "date" name = "due" disabled > < / label >
< button type = "submit" > Hinzufügen< / button >
< / form >
< form hx-post = "/api/scenarios/{{ sc.id }}/project" hx-swap = "none"
hx-on::after-request = "if(event.detail.successful){window.location.reload()}" >
@@ -450,11 +486,37 @@
recSelect . disabled = isCategory ;
}
function onModKind Change ( select ) {
function onEntryArt Change ( select ) {
// Umschaltmatrix "Neuer Eintrag"/Modifikator-Edit: Felder nur per
// disabled schalten (UX-Regel). Fehlt ein Feld (Edit-Formulare haben
// kein name/amount/due), wird es uebersprungen.
var form = select . closest ( 'form' ) ;
var isEnde = select . value === 'ende' ;
form . querySelector ( '[name="end_date"]' ) . disabled = ! isEnde ;
form . querySelector ( '[ name="value"]' ) . disabled = isEnde ;
var art = select . value ;
var isMod = art !== 'einmal' ;
function setDisabled ( name, disabled ) {
var el = form . querySelector ( '[name="' + name + '"]' ) ;
if ( el ) { el . disabled = disabled ; }
}
setDisabled ( 'value' , ! ( art === 'percent' || art === 'absolute' ) ) ;
setDisabled ( 'end_date' , art !== 'ende' ) ;
setDisabled ( 'name' , isMod ) ;
setDisabled ( 'amount' , isMod ) ;
setDisabled ( 'due' , isMod ) ;
var typeSelect = form . querySelector ( '[name="target_type"]' ) ;
if ( typeSelect ) {
typeSelect . disabled = ! isMod ;
var isCategory = typeSelect . value === 'category' ;
var catSelect = form . querySelector ( '.mod-target-category select' ) ;
var recSelect = form . querySelector ( '.mod-target-recurring select' ) ;
if ( catSelect ) { catSelect . disabled = ! isMod || ! isCategory ; }
if ( recSelect ) { recSelect . disabled = ! isMod || isCategory ; }
}
// POST-Ziel nur beim Anlege-Formular wechseln (Edit-Formulare patchen fix).
if ( form . hasAttribute ( 'data-planned-url' ) ) {
form . setAttribute ( 'hx-post' , isMod ? form . getAttribute ( 'data-modifiers-url' )
: form . getAttribute ( 'data-planned-url' ) ) ;
htmx . process ( form ) ;
}
}
function toggleScenarioLoan ( scenarioId , loanId , checked , checkbox ) {