1435 lines
53 KiB
HTML
1435 lines
53 KiB
HTML
{% 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 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 Bündel
|
||
{% elif rownum == 8 %}2 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 1–9 (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 %} |