v1.8.14: Abschlags-Tracker (monatliche Rate + Grundpreis)
Neues optionales Feature: Abschlags-Übersicht im Energie-Dashboard. Zeigt Bereits bezahlt, Grundpreis anteilig, Energiekosten sowie voraussichtliche Nachzahlung oder Guthaben für das laufende Abrechnungsjahr. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
5ab8ee75fb
commit
fec49ec4fb
@@ -254,6 +254,23 @@
|
||||
</div>
|
||||
<div style="font-size:11px;color:var(--text-dim);margin-top:4px">z.B. 1 . 4 = 01. April</div>
|
||||
</div>
|
||||
<div class="field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:4px">
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
|
||||
<input type="checkbox" id="cfg-billing-tracker" style="width:auto;margin:0" onchange="updateBillingTrackerUI()">
|
||||
<label for="cfg-billing-tracker" style="margin:0;cursor:pointer;font-size:13px;font-weight:600">Abschlags-Tracker aktivieren</label>
|
||||
</div>
|
||||
<div id="billing-tracker-fields" style="display:none">
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
|
||||
<div><label style="font-size:11px;color:var(--text-dim)">Monatliche Rate (€)</label>
|
||||
<input type="number" id="cfg-monthly-rate" step="0.01" min="0" placeholder="120.00">
|
||||
</div>
|
||||
<div><label style="font-size:11px;color:var(--text-dim)">Grundpreis pro Monat (€)</label>
|
||||
<input type="number" id="cfg-grundpreis" step="0.01" min="0" placeholder="12.50">
|
||||
</div>
|
||||
</div>
|
||||
<div style="font-size:11px;color:var(--text-dim);margin-top:6px">Zeigt Nachzahlung / Guthaben für das laufende Abrechnungsjahr.</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="savePrices()">Speichern</button>
|
||||
</div>
|
||||
|
||||
@@ -648,9 +665,43 @@ function renderEnergy(inverters, aggregates, period, spotData) {
|
||||
|
||||
const spotHtml = (period.spot_chart !== false) ? renderSpotChart(spotData) : '';
|
||||
|
||||
const bt = period.billing_tracker;
|
||||
const trackerHtml = bt ? (() => {
|
||||
const nz = bt.nachzahlung_eur;
|
||||
const isNachzahlung = nz > 0;
|
||||
const nzColor = isNachzahlung ? '#e05c5c' : '#4caf82';
|
||||
const nzLabel = isNachzahlung ? 'Voraussichtliche Nachzahlung' : 'Voraussichtliches Guthaben';
|
||||
const nzSign = isNachzahlung ? '+' : '−';
|
||||
return `<div style="margin-top:16px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px 16px">
|
||||
<div style="font-size:10px;font-weight:700;letter-spacing:.08em;color:var(--text-dim);text-transform:uppercase;margin-bottom:12px">Abschlags-Übersicht · ${bt.months_elapsed} Monate</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px">
|
||||
<div class="kwh-card">
|
||||
<div class="kv">${fEur(bt.total_paid_eur)}</div>
|
||||
<div class="kl">Bereits bezahlt<br><span style="opacity:.6">${bt.months_elapsed} × ${fEur(bt.monthly_rate_eur)}/Monat</span></div>
|
||||
</div>
|
||||
<div class="kwh-card">
|
||||
<div class="kv">${fEur(bt.grundpreis_total_eur)}</div>
|
||||
<div class="kl">Grundpreis anteilig<br><span style="opacity:.6">${fEur(bt.grundpreis_eur_per_month)}/Monat</span></div>
|
||||
</div>
|
||||
<div class="kwh-card">
|
||||
<div class="kv">${fEur(bt.energy_cost_eur)}</div>
|
||||
<div class="kl">Energiekosten<br><span style="opacity:.6">Netzbezug</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;background:var(--bg);border-radius:8px;padding:12px 16px">
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--text-dim)">${nzLabel}</div>
|
||||
<div style="font-size:11px;color:var(--text-dim);margin-top:2px">Gesamtkosten ${fEur(bt.total_cost_eur)} − Bezahlt ${fEur(bt.total_paid_eur)}</div>
|
||||
</div>
|
||||
<div style="font-size:24px;font-weight:700;color:${nzColor};font-variant-numeric:tabular-nums">${nzSign}${fEur(Math.abs(nz))}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
})() : '';
|
||||
|
||||
el.innerHTML = `<div class="energy-wrap">
|
||||
<div class="energy-svg-wrap">${svg}${spotHtml}</div>
|
||||
${cards ? `<div style="margin-top:16px">${cards}</div>` : ''}
|
||||
${trackerHtml}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -894,6 +945,11 @@ function updateTariffUI() {
|
||||
document.getElementById("tariff-spot-fields").style.display = isSpot ? "" : "none";
|
||||
}
|
||||
|
||||
function updateBillingTrackerUI() {
|
||||
const enabled = document.getElementById("cfg-billing-tracker").checked;
|
||||
document.getElementById("billing-tracker-fields").style.display = enabled ? "" : "none";
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
const cfg = await fetchJSON(api("api/config"));
|
||||
globalConfig = cfg;
|
||||
@@ -910,6 +966,11 @@ async function loadSettings() {
|
||||
document.getElementById("cfg-spot-chart").checked = cfg.spot_chart ?? true;
|
||||
document.getElementById((cfg.tariff_type ?? "fixed") === "spot" ? "tariff-spot" : "tariff-fixed").checked = true;
|
||||
updateTariffUI();
|
||||
const trackerEnabled = cfg.billing_tracker_enabled ?? false;
|
||||
document.getElementById("cfg-billing-tracker").checked = trackerEnabled;
|
||||
document.getElementById("billing-tracker-fields").style.display = trackerEnabled ? "" : "none";
|
||||
document.getElementById("cfg-monthly-rate").value = cfg.monthly_rate_eur ?? 0;
|
||||
document.getElementById("cfg-grundpreis").value = cfg.grundpreis_eur_per_month ?? 0;
|
||||
}
|
||||
|
||||
async function savePrices() {
|
||||
@@ -923,6 +984,9 @@ async function savePrices() {
|
||||
spot_chart: document.getElementById("cfg-spot-chart").checked,
|
||||
billing_day: parseInt(document.getElementById("cfg-billing-day").value),
|
||||
billing_month: parseInt(document.getElementById("cfg-billing-month").value),
|
||||
billing_tracker_enabled: document.getElementById("cfg-billing-tracker").checked,
|
||||
monthly_rate_eur: parseFloat(document.getElementById("cfg-monthly-rate").value || 0),
|
||||
grundpreis_eur_per_month: parseFloat(document.getElementById("cfg-grundpreis").value || 0),
|
||||
};
|
||||
try {
|
||||
await fetchJSON(api("api/config"), {
|
||||
|
||||
Reference in New Issue
Block a user