Files
he-database/sheets/templates/monthly_sheet.html

1435 lines
53 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 content %}
<div class="spreadsheet-container">
<!-- Navigation Header -->
<div class="sheet-navigation">
{# Previous month link #}
{% with pm=prev_month %}
{% if pm.year and pm.month %}
<a href="{% url 'monthly_sheet' pm.year pm.month %}">← Previous</a>
{% else %}
<span class="disabled-link">← Previous</span>
{% endif %}
{% endwith %}
<h2>{{ year }} - {{ month_name }} (Sheet {{ month }}/6)</h2>
{# Next month link #}
{% with nm=next_month %}
{% if nm.year and nm.month %}
<a href="{% url 'monthly_sheet' nm.year nm.month %}">Next →</a>
{% else %}
<span class="disabled-link">Next →</span>
{% endif %}
{% endwith %}
<a href="{% url 'summary_sheet' year month %}">Go to Summary</a>
</div>
<!-- Top Tables Container -->
<div class="top-tables">
<!-- Left Table (18 rows × clients) -->
<div class="table-container top-left-table">
<h3>Table 1: Top Left</h3>
<table class="spreadsheet-table">
<thead>
<tr>
<th>Row Label</th>
{% for header in top_left_headers %}
<th>{{ header }}</th>
{% endfor %}
<th>Sum</th>
</tr>
</thead>
<tbody>
{% for row in top_left_rows %}
{% with rownum=forloop.counter %}
<tr data-row="{{ forloop.counter0 }}">
<td class="row-label">
{% if rownum == 1 %}
Stand der Gaszähler (Nm³)
{% elif rownum == 2 %}
Stand der Gaszähler (Vormonat) (Nm³)
{% elif rownum == 3 %}
Gasrückführung (Nm³)
{% elif rownum == 4 %}
Rückführung flüssig (Lit. L-He)
{% elif rownum == 5 %}
Sonderrückführungen (Lit. L-He)
{% elif rownum == 6 %}
Bestand in Kannen-1 (Lit. L-He)
{% elif rownum == 7 %}
Summe Bestand (Lit. L-He)
{% elif rownum == 8 %}
Best. in Kannen Vormonat (Lit. L-He)
{% elif rownum == 9 %}
Bezug (Liter L-He)
{% elif rownum == 10 %}
Rückführ. Soll (Lit. L-He)
{% elif rownum == 11 %}
Verluste (Soll-Rückf.) (Lit. L-He)
{% elif rownum == 12 %}
Füllungen warm (Lit. L-He)
{% elif rownum == 13 %}
Kaltgas Rückgabe (Lit. L-He) Faktor
{% elif rownum == 14 %}
Faktor 0.06
{% elif rownum == 15 %}
Verbraucherverluste (Liter L-He)
{% elif rownum == 16 %}
%
{% elif rownum == 17 %}
Gesamtrückführung (Nm³)
{% elif rownum == 18 %}
Aufgeteilte Verluste (Liter L-He)
{% endif %}
</td>
{% for cell in row.cells %}
{% if is_start_sheet or rownum == 1 or rownum == 5 or rownum == 6 %}
{# Editable in start sheet OR always editable rows (B3, B7, B8) #}
<td class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="top_left"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% else %}
{# Readonly for non-start sheets #}
<td class="readonly-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="top_left"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="false">
{{ cell.value|default:"" }}
</td>
{% endif %}
{% endfor %}
<td class="sum-cell">
{{ row.sum|default_if_none:"" }}
</td>
</tr>
{% endwith %}
{% endfor %}
</tbody>
</table>
</div>
<!-- Right Table (24 rows × 6 clients) -->
<!-- Update the top-right table section in monthly_sheet.html -->
<div class="table-container top-right-table">
<h3>Table 2: Top Right</h3>
<table class="spreadsheet-table">
<thead>
<tr>
<th>Row Label</th>
{% for header in top_right_headers %}
<th>{{ header }}</th>
{% endfor %}
<th>Sum</th>
</tr>
</thead>
<tbody>
{% for row in top_right_rows %}
{% with rownum=forloop.counter %}
<tr data-row="{{ forloop.counter0 }}">
<td class="row-label">
{% if rownum == 1 %}
Stand der Gaszähler (Vormonat) (Nm³)
{% elif rownum == 2 %}
Gasrückführung (Nm³)
{% elif rownum == 3 %}
Rückführung flüssig (Lit. L-He)
{% elif rownum == 4 %}
Sonderrückführungen (Lit. L-He)
{% elif rownum == 5 %}
Sammelrückführungen (Lit. L-He)
{% elif rownum == 6 %}
Bestand in Kannen-1 (Lit. L-He)
{% elif rownum == 7 %}
Summe Bestand (Lit. L-He)
{% elif rownum == 8 %}
Best. in Kannen Vormonat (Lit. L-He)
{% elif rownum == 9 %}
Bezug (Liter L-He)
{% elif rownum == 10 %}
Rückführ. Soll (Lit. L-He)
{% elif rownum == 11 %}
Verluste (Soll-Rückf.) (Lit. L-He)
{% elif rownum == 12 %}
Füllungen warm (Lit. L-He)
{% elif rownum == 13 %}
Kaltgas Rückgabe (Lit. L-He) Faktor
{% elif rownum == 14 %}
Faktor 0.06
{% elif rownum == 15 %}
Verbraucherverluste (Liter L-He)
{% elif rownum == 16 %}
%
{% endif %}
</td>
{% for cell in row.cells %}
{% with client_name=cell.client.name|default:"" %}
{# Determine if this cell should be editable #}
{% if is_start_sheet %}
<td class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="top_right"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% elif rownum == 4 or rownum == 6 or rownum == 1 and client_name in "M3 Thiele,M3 Buntkowsky,M3 Gutfleisch" %}
<td class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="top_right"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% else %}
<td class="readonly-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="top_right"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="false">
{% if rownum == 2 %}
Aufteilung Nach Verbrauch
{% else %}
{{ cell.value|default:"" }}
{% endif %}
</td>
{% endif %}
{% endwith %}
{% endfor %}
<td class="sum-cell">
{{ row.sum|default_if_none:"" }}
</td>
</tr>
{% endwith %}
{% endfor %}
</tbody>
</table>
</div>
<div class="table-container overall-summary-table">
<h3>Gesamtsumme (Top Left + Top Right)</h3>
<table class="spreadsheet-table">
<thead>
<tr>
<th>Row Label</th>
<th>Σ</th>
</tr>
</thead>
<tbody>
{% for row in summary_rows %}
<tr data-row-index="{{ row.row_index }}">
<td class="row-label">{{ row.label }}</td>
<td class="sum-cell overall-sum-cell">
{{ row.sum|default_if_none:"" }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Bottom Tables -->
<div class="bottom-tables">
<div class="table-container bottom-table-1">
<h3>Bottom Table 1 Gasspeicher</h3>
<table class="spreadsheet-table">
<thead>
<tr>
<th>Gasspeicher</th>
<th>Volumen</th>
<th>bar</th>
<th>korrigiert</th>
<th>Nm³</th>
<th>Lit. LHe</th>
</tr>
</thead>
<tbody>
{% for row in cells_by_table.bottom_1 %}
{% with rownum=forloop.counter %}
<tr data-row="{{ forloop.counter0 }}">
<td class="row-label">
{% if rownum == 1 %}Batterie&nbsp;1
{% elif rownum == 2 %}2
{% elif rownum == 3 %}3
{% elif rownum == 4 %}4
{% elif rownum == 5 %}5
{% elif rownum == 6 %}6
{% elif rownum == 7 %}2&nbsp;Bündel
{% elif rownum == 8 %}2&nbsp;Ballone
{% elif rownum == 9 %}Reingasspeicher
{% elif rownum == 10 %}Gasbestand
{% endif %}
</td>
{% for cell in row %}
{% if forloop.counter0 < 5 %}
{% if forloop.counter0 == 0 %}
{# Volumen fixed, not editable #}
<td class="readonly-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_1"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}">
{% if cell.value %}
{{ cell.value }}
{% else %}
{% if rownum == 1 %}2,4
{% elif rownum == 2 %}5,1
{% elif rownum == 3 %}4,0
{% elif rownum == 4 %}1,0
{% elif rownum == 5 %}4,0
{% elif rownum == 6 %}0,4
{% elif rownum == 7 %}1,2
{% elif rownum == 8 %}20,0
{% elif rownum == 9 %}5,0
{% endif %}
{% endif %}
</td>
{% elif forloop.counter0 == 1 %}
{# bar editable only for rows 19 (not Gasbestand) #}
{% if rownum < 10 %}
<td class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_1"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% else %}
<td class="readonly-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_1"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}">
{{ cell.value|default:"" }}
</td>
{% endif %}
{% else %}
{# korrigiert, Nm³, Lit. LHe calculated, not editable #}
<td class="readonly-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_1"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}">
{{ cell.value|default:"" }}
</td>
{% endif %}
{% endif %}
{% endfor %}
</tr>
{% endwith %}
{% endfor %}
</tbody>
</table>
</div>
<div class="table-container bottom-table-2">
<h3>Bottom Table 2 Verbraucherbestand L-He</h3>
<table class="spreadsheet-table">
<tbody>
{# Excel row 38: header line with J38/K38 #}
<tr data-row-index="38">
<td class="row-label" colspan="5">Verbraucherbestand L-He</td>
<td id="bt2-j38" class="readonly-cell"></td>
<td id="bt2-k38" class="readonly-cell"></td>
</tr>
{# Excel row 39: + Anlage G39 / I39 are the ONLY editable inputs #}
<tr data-row-index="39">
<td class="row-label">+ Anlage</td>
<td>Gefäss 2,5</td>
{% with cell=cells_by_table.bottom_2.0.0 %}
<td id="bt2-g39"
class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_2"
data-row="0"
data-col="0"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% endwith %}
<td>Gefäss 1,0</td>
{% with cell=cells_by_table.bottom_2.0.1 %}
<td id="bt2-i39"
class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_2"
data-row="0"
data-col="1"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% endwith %}
<td id="bt2-j39" class="readonly-cell"></td>
<td id="bt2-k39" class="readonly-cell"></td>
</tr>
{# Excel row 40: + Kaltgas all calculated #}
<tr data-row-index="40">
<td class="row-label">+ Kaltgas</td>
<td>Gefäss 2,5</td>
<td id="bt2-g40" class="readonly-cell"></td>
<td>Gefäss 1,0</td>
<td id="bt2-i40" class="readonly-cell"></td>
<td id="bt2-j40" class="readonly-cell"></td>
<td id="bt2-k40" class="readonly-cell"></td>
</tr>
{# Excel row 43: Bestand flüssig He uses K38..K40 #}
<tr data-row-index="43">
<td class="row-label" colspan="5">Bestand flüssig He</td>
<td id="bt2-j43" class="readonly-cell"></td>
<td id="bt2-k43" class="readonly-cell"></td>
</tr>
{# Excel row 44: Gesamtbestand neu Gasbestand (bottom 1) + K43 #}
<tr data-row-index="44">
<td class="row-label" colspan="5">Gesamtbestand neu</td>
<td id="bt2-j44" class="readonly-cell"></td>
<td id="bt2-k44" class="readonly-cell"></td>
</tr>
</tbody>
</table>
</div>
<div class="table-container bottom-table-3">
<h3>Bottom Table 3 Bilanz</h3>
<table class="spreadsheet-table">
<thead>
<tr>
<th>Bezeichnung</th>
<th>F</th>
<th>G</th>
<th>I</th>
<th>Nm³ (J)</th>
<th>Lit. L-He (K)</th>
</tr>
</thead>
<tbody>
{# Row 46: Gesamtbestand Vormonat #}
<tr data-row="46">
<td class="row-label">Gesamtbestand Vormonat</td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
{# J46 comes from bottom_3 row 0, col 3 #}
{% with cell=cells_by_table.bottom_3.0.3 %}
<td id="bt3-j46"
class="readonly-cell calculated-cell"
data-cell-id="{{ cell.id|default:'' }}">
{{ cell.value|default:"" }}
</td>
{% endwith %}
{# K46 comes from bottom_3 row 0, col 4 (prev month K44) #}
{% with cell=cells_by_table.bottom_3.0.4 %}
<td id="bt3-k46"
class="readonly-cell"
{% if prev_summary %}
data-prev-k44="{{ prev_summary.gesamtbestand_neu_lhe|default_if_none:'' }}"
{% endif %}>
{% if prev_summary %}
{{ prev_summary.gesamtbestand_neu_lhe|default_if_none:'' }}
{% endif %}
</td>
{% endwith %}
</tr>
{# Row 47: + Verbrauch / Anlage #}
<tr data-row="47">
<td class="row-label">+ Verbrauch / Anlage</td>
<!-- F47: empty, readonly -->
<td id="bt3-f47" class="readonly-cell"></td>
<!-- G47 editable -->
{% with cell=cells_by_table.bottom_3.1.1 %}
<td id="bt3-g47"
class="editable-cell"
data-table="bottom_3"
data-row="1"
data-col="1"
data-cell-id="{{ cell.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% endwith %}
<!-- I47 editable -->
{% with cell=cells_by_table.bottom_3.1.2 %}
<td id="bt3-i47"
class="editable-cell"
data-table="bottom_3"
data-row="1"
data-col="2"
data-cell-id="{{ cell.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% endwith %}
<td id="bt3-j47" class="readonly-cell"></td>
<td id="bt3-k47" class="readonly-cell"></td>
</tr>
{# Row 48: K48 = K46 + K47, J48 = K48 * 0.75 #}
<tr data-row="48">
<td class="row-label">Gesamtbestand inkl. Anlage</td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td id="bt3-j48" class="readonly-cell"></td>
<td id="bt3-k48" class="readonly-cell"></td>
</tr>
{# Row 49: K49 = current month K44, J49 = K49 * 0.75 (will fill via JS) #}
<tr data-row="49">
<td class="row-label">Gesamtbestand (akt. Monat)</td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td id="bt3-j49" class="readonly-cell"></td>
<td id="bt3-k49" class="readonly-cell"></td>
</tr>
{# Row 50: I50 editable, J50/K50 readonly #}
<tr data-row="50">
<td class="row-label">Verbrauch Sonstiges</td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
{% with cell=cells_by_table.bottom_3.4.2 %}
<td id="bt3-i50"
class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_3"
data-row="4"
data-col="2"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% endwith %}
<td id="bt3-j50" class="readonly-cell"></td>
<td id="bt3-k50" class="readonly-cell"></td>
</tr>
{# Row 51: K51 = K48 - K49 - K50, J51 = K51 * 0.75 #}
<tr data-row="51">
<td class="row-label">Differenz Bestand</td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td id="bt3-j51" class="readonly-cell"></td>
<td id="bt3-k51" class="readonly-cell"></td>
</tr>
{# Row 52: K52 = Verbraucherverluste (aus Übersicht), J52 = K52 * 0.75 #}
<tr data-row="52">
<td class="row-label">Verbraucherverluste (aus Übersicht)</td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td id="bt3-j52" class="readonly-cell"></td>
<td id="bt3-k52" class="readonly-cell"></td>
</tr>
{# Row 53: J53 = J51 - J52, K53 = K51 - K52 #}
<tr data-row="53">
<td class="row-label">Differenz Bilanz</td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td class="readonly-cell"></td>
<td id="bt3-j53" class="readonly-cell"></td>
<td id="bt3-k53" class="readonly-cell"></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Save Button -->
<div class="action-bar">
<button id="save-all-btn" class="btn btn-primary">Save All Cells</button>
<div id="save-status"></div>
<div class="legend">
<small>
<span style="background-color: #d1ecf1; padding: 2px 5px;">Saved cells</span>
<span style="background-color: #d4edda; padding: 2px 5px;">Calculated cells</span>
</small>
</div>
</div>
</div>
<!-- Hidden form for cell data -->
<form id="cell-data-form" style="display: none;">
{% csrf_token %}
<input type="hidden" name="sheet_id" value="{{ sheet.id }}">
</form>
<style>
.spreadsheet-container {
padding: 20px;
}
.sheet-navigation {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 10px;
background-color: #f8f9fa;
border-radius: 5px;
}
.sheet-navigation h2 {
margin: 0;
}
.top-tables {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.top-tables .table-container {
flex: 1;
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
background-color: white;
}
.merged-cell-left {
border-right-width: 0;
}
.merged-cell-right {
border-left-width: 0;
}
.cell-group-LM {
background-color: #f0f8ff; /* Light blue for L&M group */
}
.cell-group-NO {
background-color: #f0fff0; /* Light green for N&O group */
}
.cell-group-PQR {
background-color: #fff0f0; /* Light red for P,Q,R group */
}
.bottom-tables {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-bottom: 20px;
}
.bottom-tables .table-container {
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
background-color: white;
}
.spreadsheet-table {
width: 100%;
border-collapse: collapse;
}
.spreadsheet-table th,
.spreadsheet-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: center;
}
.spreadsheet-table th {
background-color: #f2f2f2;
font-weight: bold;
}
.row-label {
background-color: #f9f9f9;
font-weight: bold;
}
.editable-cell {
min-width: 100px;
min-height: 30px;
background-color: white;
cursor: text;
}
.editable-cell:focus {
outline: 2px solid #007bff;
background-color: #f0f8ff;
}
.editable-cell:hover {
background-color: #f1f1f1;
}
.readonly-cell {
background-color: #f8f9fa;
color: #495057;
cursor: default;
min-height: 30px;
}
.cell-calculated {
font-style: italic;
font-weight: bold;
background-color: #e8f4f8;
}
.sum-cell {
font-weight: bold;
background-color: #f0f0f0;
padding: 5px;
}
.action-bar {
display: flex;
gap: 10px;
align-items: center;
padding: 10px;
background-color: #f8f9fa;
border-radius: 5px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.btn-primary {
background-color: #007bff;
color: white;
}
#save-status {
margin-left: 10px;
color: #28a745;
font-weight: bold;
}
.saving-text {
position: absolute;
font-size: 11px;
color: #666;
margin-left: 5px;
}
.saving {
background-color: #fff3cd !important;
font-style: italic;
}
.saved-success {
background-color: #d4edda !important;
transition: background-color 0.5s ease;
}
.calculated-cell {
background-color: #e8f4f8 !important;
font-style: italic;
color: #0c5460;
}
.editable-cell[contenteditable="false"] {
background-color: #f8f9fa !important;
cursor: not-allowed;
}
</style>
<script>
$(document).ready(function() {
// ---------- Helpers ----------
function parseNumber(text) {
const t = (text || '').trim().replace(',', '.');
if (t === '') return null;
const v = parseFloat(t);
return isNaN(v) ? null : v;
}
// Recalculate Sum cell (last column) for a given row
function recalcRowSum($row) {
let total = 0;
let hasValue = false;
const $table = $row.closest('table');
const isTopRight = $table.closest('.top-right-table').length > 0;
// "data-row" is rendered on <tr data-row="...">
const rowIndex = parseInt($row.data('row'), 10);
const mergedRows = [2, 3, 5, 6, 7, 9, 10, 12, 14, 15];
const skipCols = [1, 3]; // 0-based indices inside the client columns
$row.find('td').each(function(colIndex) {
// don't touch labels / sum cells
if ($(this).hasClass('row-label') || $(this).hasClass('sum-cell')) {
return;
}
// Skip second cell of each merged pair in top-right merged rows
if (isTopRight &&
mergedRows.indexOf(rowIndex) !== -1 &&
skipCols.indexOf(colIndex - 1) !== -1) {
// colIndex - 1 because first <td> is the row label
return;
}
const text = $(this).text().trim();
const val = parseFloat(text.replace(',', '.'));
if (!isNaN(val)) {
total += val;
hasValue = true;
}
});
if (hasValue) {
$row.find('.sum-cell').text(total);
} else {
$row.find('.sum-cell').text('');
}
}
// ---------- % row (top tables) ----------
function recalcPercentTotals($tables) {
$tables.each(function() {
const $table = $(this);
function findRowByLabel(labelPrefix) {
return $table.find('tbody tr').filter(function() {
const label = $(this).find('.row-label').text().trim();
return label.indexOf(labelPrefix) === 0;
}).first();
}
const $rowBezug = findRowByLabel('Bezug (Liter L-He)');
const $rowVerb = findRowByLabel('Verbraucherverluste (Liter L-He)');
const $rowPerc = findRowByLabel('%');
if (!$rowBezug.length || !$rowVerb.length || !$rowPerc.length) {
return;
}
const sumBezug = parseNumber($rowBezug.find('.sum-cell').text());
const sumVerb = parseNumber($rowVerb.find('.sum-cell').text());
const $percSumCell = $rowPerc.find('.sum-cell');
if (sumBezug !== null && sumBezug !== 0 && sumVerb !== null) {
const perc = sumVerb / sumBezug;
$percSumCell.text(perc);
} else {
$percSumCell.text('');
}
});
}
// ---------- Overall Σ table (between top-right and bottom-1) ----------
function getRowSumFromSingleTable(tableSelector, rowIndex) {
const $row = $(tableSelector + ' tbody tr[data-row="' + rowIndex + '"]');
if (!$row.length) return null;
return parseNumber($row.find('.sum-cell').text());
}
function getRowSumFromTables(rowIndex) {
const left = getRowSumFromSingleTable('.top-left-table', rowIndex);
const right = getRowSumFromSingleTable('.top-right-table', rowIndex);
if (left === null && right === null) return null;
return (left || 0) + (right || 0);
}
function recalcOverallSummary() {
const $summaryTable = $('.overall-summary-table .spreadsheet-table');
if (!$summaryTable.length) return;
$summaryTable.find('tbody tr').each(function() {
const $row = $(this);
const rowIndex = parseInt($row.data('row-index'), 10);
if (isNaN(rowIndex)) return;
let value = null;
// Faktor row (fixed 0.06)
if (rowIndex === 13) {
value = 0.06;
// % row: total Verbraucherverluste / total Bezug
} else if (rowIndex === 15) {
const verbValue = parseNumber(
$summaryTable.find('tr[data-row-index="14"] .sum-cell').text()
);
const bezugValue = parseNumber(
$summaryTable.find('tr[data-row-index="8"] .sum-cell').text()
);
if (bezugValue !== null && bezugValue !== 0 && verbValue !== null) {
value = verbValue / bezugValue;
} else {
value = null;
}
// Sammelrückführungen: only from top-right
} else if (rowIndex === 4) {
const right = getRowSumFromSingleTable('.top-right-table', rowIndex);
value = (right !== null) ? right : null;
// All other rows: sum of the two tables
} else {
value = getRowSumFromTables(rowIndex);
}
const $sumCell = $row.find('.sum-cell');
$sumCell.text(value === null ? '' : value);
});
}
// ---------- Bottom Table 2 (Verbraucherbestand L-He) ----------
function recalcBottom2() {
const $summaryTable = $('.overall-summary-table .spreadsheet-table');
function getSummaryRowValue(rowIndex) {
if (!$summaryTable.length) return null;
const txt = $summaryTable
.find('tr[data-row-index="' + rowIndex + '"] .sum-cell')
.text();
return parseNumber(txt);
}
// --- K38 / J38 ---
let k38 = getSummaryRowValue(6); // Summe Bestand (Lit. L-He)
if (k38 !== null) {
$('#bt2-k38').text(k38);
$('#bt2-j38').text(k38 * 0.75);
} else {
$('#bt2-k38').text('');
$('#bt2-j38').text('');
}
// --- Inputs G39 / I39 (editable) ---
const g39 = parseNumber($('#bt2-g39').text());
const i39 = parseNumber($('#bt2-i39').text());
// K39 = G39 + I39
let k39 = null;
if (g39 !== null || i39 !== null) {
k39 = (g39 || 0) + (i39 || 0);
}
if (k39 !== null) {
$('#bt2-k39').text(k39);
$('#bt2-j39').text(k39 * 0.75);
} else {
$('#bt2-k39').text('');
$('#bt2-j39').text('');
}
// --- + Kaltgas (G40 / I40 / K40 / J40) ---
let g40 = null, i40 = null, k40 = null;
if (g39 !== null) {
g40 = (2500 - g39) / 100 * 10;
}
if (i39 !== null) {
i40 = (1000 - i39) / 100 * 10;
}
if (g40 !== null || i40 !== null) {
k40 = (g40 || 0) + (i40 || 0);
}
$('#bt2-g40').text(g40 !== null ? g40 : '');
$('#bt2-i40').text(i40 !== null ? i40 : '');
$('#bt2-k40').text(k40 !== null ? k40 : '');
$('#bt2-j40').text(k40 !== null ? k40 * 0.75 : '');
// --- Bestand flüssig He (row 43) ---
const k43 = (k38 || 0) + (k39 || 0) + (k40 || 0);
$('#bt2-k43').text(k43 ? k43 : '');
$('#bt2-j43').text(k43 ? k43 * 0.75 : '');
// --- Gesamtbestand neu (row 44) ---
let gasbestandLit = null;
const $gasRow = $('.bottom-table-1 tbody tr').filter(function() {
const txt = $(this).find('.row-label').text().trim();
return txt.indexOf('Gasbestand') === 0;
}).first();
if ($gasRow.length) {
const lastTxt = $gasRow.find('td').last().text();
gasbestandLit = parseNumber(lastTxt);
}
let k44 = null;
if (gasbestandLit !== null || k43 !== null) {
k44 = (gasbestandLit || 0) + (k43 || 0);
}
$('#bt2-k44').text(k44 !== null ? k44 : '');
$('#bt2-j44').text(k44 !== null ? k44 * 0.75 : '');
}
// (Optional) If you later add JS-side bottom_3 logic, you can define recalcBottom3 here.
function recalcBottom3() {
const factor = 0.75;
// Small helper
function p(sel) {
return parseNumber($(sel).text());
}
// ---- 1) Row 46: Gesamtbestand Vormonat ----
// Get prev K44 from data attribute OR text content
let prevK44 = null;
const $k46 = $('#bt3-k46');
// First try to get from data attribute
const dataPrev = $k46.data('prevK44'); // jQuery automatically converts kebab-case to camelCase
if (dataPrev !== undefined && dataPrev !== null && dataPrev !== '') {
prevK44 = parseNumber(String(dataPrev));
}
// If still null, try text content
if (prevK44 === null) {
const textContent = $k46.text().trim();
if (textContent !== '') {
prevK44 = parseNumber(textContent);
}
}
// Now calculate J46 = K46 * 0.75
if (prevK44 !== null) {
$('#bt3-j46').text(prevK44 * factor);
} else {
$('#bt3-j46').text('');
}
// ---- 2) Row 47: G47 / I47 editable ----
// J47 = I47 + G47
// K47 = J47 / 0.75 + G47 (you corrected F47 to be empty)
const g47 = p('#bt3-g47');
const i47 = p('#bt3-i47');
let j47 = null;
let k47 = null;
if (g47 !== null || i47 !== null) {
j47 = (g47 || 0) + (i47 || 0);
k47 = j47 / factor + (g47 || 0);
}
$('#bt3-j47').text(j47 !== null ? j47 : '');
$('#bt3-k47').text(k47 !== null ? k47 : '');
// ---- 3) Row 48 ----
// K48 = K46 + K47
// J48 = K48 * 0.75
let k48 = null;
if (prevK44 !== null || k47 !== null) {
k48 = (prevK44 || 0) + (k47 || 0);
}
$('#bt3-k48').text(k48 !== null ? k48 : '');
$('#bt3-j48').text(k48 !== null ? k48 * factor : '');
// ---- 4) Row 49 ----
// K49 = current month K44 (bottom table 2: bt2-k44)
// J49 = K49 * 0.75
const curK44 = p('#bt2-k44');
if (curK44 !== null) {
$('#bt3-k49').text(curK44);
$('#bt3-j49').text(curK44 * factor);
} else {
$('#bt3-k49').text('');
$('#bt3-j49').text('');
}
// ---- 5) Row 50 ----
// J50 = I50
// K50 = J50 / 0.75
const i50 = p('#bt3-i50');
let j50 = null;
let k50 = null;
if (i50 !== null) {
j50 = i50;
k50 = j50 / factor;
}
$('#bt3-j50').text(j50 !== null ? j50 : '');
$('#bt3-k50').text(k50 !== null ? k50 : '');
// ---- 6) Row 51 ----
// K51 = K48 - K49 - K50
// J51 = K51 * 0.75
let k51 = null;
if (k48 !== null || curK44 !== null || k50 !== null) {
k51 = (k48 || 0) - (curK44 || 0) - (k50 || 0);
}
$('#bt3-k51').text(k51 !== null ? k51 : '');
$('#bt3-j51').text(k51 !== null ? k51 * factor : '');
// ---- 7) Row 52 ----
// K52 = Verbraucherverlust from overall summary table
// J52 = K52 * 0.75
//
// In the Σ table, Verbraucherverluste row is row_index = 14
const $summaryTable = $('.overall-summary-table .spreadsheet-table');
let verbrauch = null;
if ($summaryTable.length) {
const txt = $summaryTable
.find('tr[data-row-index="14"] .sum-cell')
.text();
verbrauch = parseNumber(txt);
}
if (verbrauch !== null) {
$('#bt3-k52').text(verbrauch);
$('#bt3-j52').text(verbrauch * factor);
} else {
$('#bt3-k52').text('');
$('#bt3-j52').text('');
}
// ---- 8) Row 53 ----
// J53 = J51 - J52
// K53 = K51 - K52
const j51 = p('#bt3-j51');
const j52 = p('#bt3-j52');
const k52 = p('#bt3-k52');
let j53 = null;
let k53 = null;
if (j51 !== null || j52 !== null) {
j53 = (j51 || 0) - (j52 || 0);
}
if (k51 !== null || k52 !== null) {
k53 = (k51 || 0) - (k52 || 0);
}
$('#bt3-j53').text(j53 !== null ? j53 : '');
$('#bt3-k53').text(k53 !== null ? k53 : '');
}
// ---------- Editing handlers ----------
// store original value on focus
$('.editable-cell').on('focus', function() {
$(this).data('original-value', $(this).text().trim());
});
// save on blur
$('.editable-cell').on('blur', function() {
saveCell($(this));
});
// save on Enter
$('.editable-cell').on('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
$(this).blur();
}
});
// block editing on readonly cells
$('.readonly-cell').on('focus click', function(e) {
e.preventDefault();
$(this).blur();
});
// ---------- Single-cell save (blur) ----------
function saveCell($cell) {
const cellId = $cell.data('cell-id');
const newValue = $cell.text().trim();
const originalValue = $cell.data('original-value') || '';
// no change → nothing to do
if (newValue === originalValue) {
return;
}
// cell without ID → cannot save
if (!cellId) {
console.error('No cell ID found for', $cell.attr('id'));
return;
}
$cell.addClass('saving');
const csrfToken = $('[name=csrfmiddlewaretoken]').val();
$.ajax({
url: '{% url "save_cells" %}',
method: 'POST',
headers: {
'X-CSRFToken': csrfToken
},
data: {
'sheet_id': '{{ sheet.id }}',
'cell_id': cellId,
'value': newValue
},
success: function(response) {
$cell.removeClass('saving');
if (response.status === 'success') {
if (response.updated_cells) {
response.updated_cells.forEach(function(cellData) {
const targetCell = $('[data-cell-id="' + cellData.id + '"]');
if (!targetCell.length) return;
targetCell.text(cellData.value || '');
targetCell.data('original-value', cellData.value || '');
if (cellData.is_calculated) {
targetCell.addClass('calculated-cell');
}
const $row = targetCell.closest('tr');
if ($row.length) {
recalcRowSum($row);
}
});
}
recalcPercentTotals($('.top-left-table .spreadsheet-table'));
recalcPercentTotals($('.top-right-table .spreadsheet-table'));
recalcOverallSummary();
recalcBottom2();
recalcBottom3();
// 🔸 NEW: auto-save MonthlySummary so next month sees changes live
(function autoSaveMonthSummary() {
// If you want to only do this for bottom_2 edits, keep this:
const tableType = $cell.data('table');
if (tableType !== 'bottom_2') {
return; // remove this 'if' block if you want to save for all cells
}
// Read the *current* values after recalcBottom2/OverallSummary
const k44Text = $('#bt2-k44').text().trim(); // Gesamtbestand neu (L-He)
const gasbestandText = $('#bt1-gasbestand-lhe').text().trim();
const verbrauchText = $('.overall-summary-table .spreadsheet-table')
.find('tr[data-row-index="14"] .sum-cell')
.text().trim();
$.ajax({
url: '{% url "save_month_summary" %}',
method: 'POST',
contentType: 'application/json',
headers: {
'X-CSRFToken': csrfToken
},
data: JSON.stringify({
sheet_id: '{{ sheet.id }}',
gesamtbestand_neu_lhe: k44Text || null,
gasbestand_lhe: gasbestandText || null,
verbraucherverlust_lhe: verbrauchText || null
}),
success: function(res) {
if (!res.success) {
console.error('Summary save error:', res.message);
}
},
error: function(xhr, status, error) {
console.error('Error saving monthly summary:', error);
}
});
})();
showStatus('Saved successfully!', 'success');
$cell.addClass('saved-success');
setTimeout(function() {
$cell.removeClass('saved-success');
}, 1000);
} else {
$cell.text(originalValue);
showStatus('Error: ' + (response.message || 'Unknown error'), 'error');
}
},
error: function() {
$cell.removeClass('saving');
$cell.text(originalValue);
showStatus('Error saving cell', 'error');
}
});
}
// ---------- Save All (bulk) ----------
$('#save-all-btn').on('click', function() {
const csrfToken = $('[name=csrfmiddlewaretoken]').val();
const formData = new FormData();
formData.append('sheet_id', '{{ sheet.id }}');
formData.append('csrfmiddlewaretoken', csrfToken);
$('.editable-cell').each(function() {
const cellId = $(this).data('cell-id');
if (cellId) {
formData.append('cell_' + cellId, $(this).text().trim());
}
});
$.ajax({
url: '{% url "save_cells" %}',
method: 'POST',
data: formData,
processData: false,
contentType: false,
headers: {
'X-CSRFToken': csrfToken
},
success: function (response) {
if (response.status === 'success') {
// Update changed cells
if (response.updated_cells) {
response.updated_cells.forEach(function (cellData) {
const targetCell = $('[data-cell-id="' + cellData.id + '"]');
if (targetCell.length) {
targetCell.text(cellData.value || '');
targetCell.data('original-value', cellData.value || '');
}
});
}
// Recalculate all JS-driven tables
recalcPercentTotals($('.top-left-table .spreadsheet-table'));
recalcPercentTotals($('.top-right-table .spreadsheet-table'));
recalcOverallSummary();
recalcBottom2();
recalcBottom3();
// ---- Save Monthly Summary (K44, Gasbestand, Verbraucherverlust) ----
const k44Text = $('#bt2-k43').text().trim();
const gasbestandText = $('#bt1-gasbestand-lhe').text().trim();
const verbrauchText = $('.overall-summary-table .spreadsheet-table')
.find('tr[data-row-index="14"] .sum-cell')
.text().trim();
$.ajax({
url: '{% url "save_month_summary" %}',
method: 'POST',
contentType: 'application/json',
headers: {
'X-CSRFToken': csrfToken
},
data: JSON.stringify({
sheet_id: '{{ sheet.id }}',
gesamtbestand_neu_lhe: k44Text || null,
gasbestand_lhe: gasbestandText || null,
verbraucherverlust_lhe: verbrauchText || null
}),
success: function(res) {
if (!res.success) {
console.error('Summary save error:', res.message);
}
},
error: function(xhr, status, error) {
console.error('Error saving monthly summary:', error);
}
});
showStatus('All cells saved!', 'success');
} else {
showStatus('Error: ' + (response.message || 'Unknown error'), 'error');
}
},
error: function() {
showStatus('Error saving all cells', 'error');
}
});
});
// ---------- Status helper ----------
function showStatus(message, type) {
const statusEl = $('#save-status');
statusEl.text(message);
statusEl.css('color', type === 'success' ? '#28a745' : '#dc3545');
setTimeout(function() {
statusEl.text('');
}, 3000);
}
// ---------- Initial recalculation on page load ----------
recalcPercentTotals($('.top-left-table .spreadsheet-table'));
recalcPercentTotals($('.top-right-table .spreadsheet-table'));
recalcOverallSummary();
recalcBottom2();
recalcBottom3();
});
</script>
{% endblock %}