Finished months balance except the below tables

This commit is contained in:
2026-02-04 15:31:19 +01:00
parent 34f040df30
commit 567b9edc2d
40 changed files with 6357 additions and 58 deletions

View File

@@ -47,6 +47,7 @@
<a href="{% url 'table_two' %}" class="nav-button">Go to Helium Output</a>
<a href="/admin/" class="nav-button admin-button">Go to Admin Panel</a>
<a href="{% url 'betriebskosten_list' %}" class="nav-button">Betriebskosten</a>
<a href="{% url 'monthly_sheet' year=2024 month=1 %}">Monthly Sheets</a>
</div>
</div>

View File

@@ -0,0 +1,674 @@
{% extends "base.html" %}
{% block content %}
<div class="spreadsheet-container">
<!-- Navigation Header -->
<div class="sheet-navigation">
<a href="{% url 'monthly_sheet' prev_month.year prev_month.month %}">← Previous</a>
<h2>{{ year }} - {{ month_name }} (Sheet {{ month }}/6)</h2>
<a href="{% url 'monthly_sheet' next_month.year next_month.month %}">Next →</a>
<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>
<!-- Bottom Tables -->
<div class="bottom-tables">
<div class="table-container bottom-table-1">
<h3>Bottom Table 1</h3>
<table class="spreadsheet-table">
<thead>
<tr>
<th>Row Label</th>
{% for client in clients %}
<th>{{ client.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in cells_by_table.bottom_1 %}
<tr data-row="{{ forloop.counter0 }}">
<td class="row-label">Row {{ forloop.counter }}</td>
{% for cell in row %}
<td class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_1"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="table-container bottom-table-2">
<h3>Bottom Table 2</h3>
<table class="spreadsheet-table">
<thead>
<tr>
<th>Row Label</th>
{% for client in clients %}
<th>{{ client.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in cells_by_table.bottom_2 %}
<tr data-row="{{ forloop.counter0 }}">
<td class="row-label">Row {{ forloop.counter }}</td>
{% for cell in row %}
<td class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_2"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="table-container bottom-table-3">
<h3>Bottom Table 3</h3>
<table class="spreadsheet-table">
<thead>
<tr>
<th>Row Label</th>
{% for client in clients %}
<th>{{ client.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in cells_by_table.bottom_3 %}
<tr data-row="{{ forloop.counter0 }}">
<td class="row-label">Row {{ forloop.counter }}</td>
{% for cell in row %}
<td class="editable-cell"
data-cell-id="{{ cell.id|default:'' }}"
data-table="bottom_3"
data-row="{{ forloop.parentloop.counter0 }}"
data-col="{{ forloop.counter0 }}"
data-client-id="{{ cell.client.id|default:'' }}"
contenteditable="true">
{{ cell.value|default:"" }}
</td>
{% endfor %}
</tr>
{% endfor %}
</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() {
// Enable editing 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 key
$('.editable-cell').on('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
$(this).blur();
}
});
// Prevent editing of readonly cells
$('.readonly-cell').on('focus click', function(e) {
e.preventDefault();
$(this).blur();
});
function saveCell($cell) {
const cellId = $cell.data('cell-id');
const newValue = $cell.text().trim();
const originalValue = $cell.data('original-value') || '';
// Skip if no change
if (newValue === originalValue) {
return;
}
// Skip if no cell ID
if (!cellId) {
console.error('No cell ID found');
return;
}
// Show saving indicator
$cell.addClass('saving');
// Prepare AJAX request
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') {
// Update all cells returned by the server
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 || '');
// Style calculated cells differently
// Style calculated cells differently
if (cellData.is_calculated) {
targetCell.addClass('calculated-cell');
// Leave contenteditable as set by the template
}
}
});
}
// Show success message
showStatus('Saved successfully!', 'success');
// Highlight the saved cell briefly
$cell.addClass('saved-success');
setTimeout(function() {
$cell.removeClass('saved-success');
}, 1000);
} else {
// Restore original value on error
$cell.text(originalValue);
showStatus('Error: ' + response.message, 'error');
}
},
error: function(xhr, status, error) {
$cell.removeClass('saving');
$cell.text(originalValue);
showStatus('Error: ' + error, 'error');
console.error('Save error:', error);
}
});
}
// Save All button
$('#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);
// Collect all cell values
$('.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') {
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 || '');
}
});
}
showStatus('All cells saved!', 'success');
} else {
showStatus('Error: ' + response.message, 'error');
}
},
error: function() {
showStatus('Error saving all cells', 'error');
}
});
});
function showStatus(message, type) {
const statusEl = $('#save-status');
statusEl.text(message);
statusEl.css('color', type === 'success' ? '#28a745' : '#dc3545');
setTimeout(function() {
statusEl.text('');
}, 3000);
}
});
</script>
{% endblock %}

View File

@@ -191,15 +191,184 @@
.readonly-field {
background-color: #e9ecef;
color: #6c757d;
}
/* ---- 6-month overview card ---- */
.overview-card {
background-color: #ffffff;
padding: 16px 20px;
margin: 20px 0;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08);
}
.overview-header {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: flex-end;
margin-bottom: 12px;
}
.overview-header label {
font-size: 0.9rem;
color: #555;
}
.overview-header select {
padding: 6px 8px;
border-radius: 4px;
border: 1px solid #ccc;
}
.overview-header button {
padding: 7px 14px;
border-radius: 4px;
border: none;
background-color: #007bff;
color: #fff;
cursor: pointer;
}
.overview-header button:hover {
background-color: #0056b3;
}
.overview-title {
font-size: 1.1rem;
font-weight: bold;
margin-bottom: 8px;
color: #333;
}
.overview-subtitle {
font-size: 0.9rem;
color: #555;
margin-bottom: 8px;
}
.overview-table {
width: 100%;
border-collapse: collapse;
}
.overview-table th,
.overview-table td {
padding: 6px 8px;
border-bottom: 1px solid #eee;
font-size: 0.9rem;
}
.overview-table thead th {
background-color: #007bff;
color: #fff;
text-align: right;
}
.overview-table thead th:first-child {
text-align: left;
}
.overview-table tbody tr:hover {
background-color: #f8f9ff;
}
.overview-table .number-cell {
text-align: right;
}
.overview-table .summary-row {
background-color: #f0f4ff;
font-weight: bold;
}
</style>
</head>
<body>
<!-- This link should ONLY wrap the text below -->
<a href="{% url 'clients_list' %}" class="btn btn-outline-primary">
&#8678; Go to Clients
</a>
<!-- 6-Month overview card (OUTSIDE any <a>) -->
<div class="overview-card">
<div class="overview-title">Helium Input 6 Month Overview</div>
<form method="get" class="overview-header">
<div>
<label for="overview-year">Year</label><br>
<select name="overview_year" id="overview-year">
{% for y in available_years %}
<option value="{{ y }}"
{% if overview and overview.year == y %}selected{% endif %}>
{{ y }}
</option>
{% endfor %}
</select>
</div>
<div>
<label for="overview-start-month">Start month</label><br>
<select name="overview_start_month" id="overview-start-month">
{% for m, label in month_choices %}
<option value="{{ m }}"
{% if overview and overview.start_month == m %}selected{% endif %}>
{{ m }} - {{ label }}
</option>
{% endfor %}
</select>
</div>
<div>
<button type="submit">Show overview</button>
</div>
</form>
{% if overview %}
<div class="overview-subtitle">
Period:
<strong>{{ overview.start_month }}{{ overview.end_month }} / {{ overview.year }}</strong>
</div>
<table class="overview-table">
<thead>
<tr>
<th>Month</th>
{% for g in overview.groups %}
<th>{{ g.label }}</th>
{% endfor %}
<th>Month total</th>
</tr>
</thead>
<tbody>
{% for row in overview.rows %}
<tr>
<td>
{{ row.month_number }} - {{ row.month_label|slice:":3" }}
</td>
{% for value in row.values %}
<td class="number-cell">{{ value|floatformat:2 }}</td>
{% endfor %}
<td class="number-cell">{{ row.total|floatformat:2 }}</td>
</tr>
{% endfor %}
<tr class="summary-row">
<td>Summe</td>
{% for total in overview.group_totals %}
<td class="number-cell">{{ total|floatformat:2 }}</td>
{% endfor %}
<td class="number-cell">{{ overview.grand_total|floatformat:2 }}</td>
</tr>
</tbody>
</table>
{% else %}
<div class="overview-subtitle">
No data yet choose a year and start month and click “Show overview”.
</div>
{% endif %}
</div>
<h2>Helium Input</h2>
<div class="table-container">
<button class="add-row-btn" id="add-row-one">Add Row</button>
@@ -218,7 +387,8 @@
<col style="width: 5%"> <!-- L-He -->
<col style="width: 5%"> <!-- L-He zus. -->
<col style="width: 6%"> <!-- L-He ges. -->
<col style="width: 6%"> <!-- Date Joined -->
<col style="width: 7%"> <!-- Date -->
<col style="width: 5%"> <!-- Month -->
<col style="width: 8%"> <!-- Actions -->
</colgroup>
<thead>
@@ -236,7 +406,8 @@
<th>L-He</th>
<th>L-He zus.</th>
<th>L-He ges.</th>
<th>Date Joined</th>
<th>Date</th>
<th>Month</th>
<th>Actions</th>
</tr>
</thead>
@@ -256,7 +427,16 @@
<td>{{ entry.lhe|floatformat:6 }}</td>
<td>{{ entry.lhe_zus|floatformat:3 }}</td>
<td>{{ entry.lhe_ges|floatformat:6 }}</td>
<td>{{ entry.date_joined|date:"Y-m-d" }}</td>
<td>
{% if entry.date %}
{{ entry.date|date:"d.m.Y" }}
{% endif %}
</td>
<td>
{% if entry.date %}
{{ entry.date|date:"m" }} {# e.g. 0112 #}
{% endif %}
</td>
<td class="actions">
<button class="edit-btn-one">Edit</button>
<button class="delete-btn-one">Delete</button>
@@ -645,6 +825,7 @@
<td>${response.lhe_zus}</td>
<td>${response.lhe_ges}</td>
<td>${response.date || ''}</td>
<td>${response.month || ''}</td>
<td class="actions">
<button class="edit-btn-one">Edit</button>
<button class="delete-btn-one">Delete</button>
@@ -805,6 +986,7 @@
row.find('td:eq(11)').text(response.lhe_zus);
row.find('td:eq(12)').text(response.lhe_ges);
row.find('td:eq(13)').text(response.date || '');
row.find('td:eq(14)').text(response.month || '');
$('#edit-popup-one').fadeOut();
} else {
alert('Error: ' + (response.message || 'Failed to update entry'));