Finished months balance except the below tables
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
674
sheets/templates/monthly_sheet.html
Normal file
674
sheets/templates/monthly_sheet.html
Normal 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 %}
|
||||
@@ -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">
|
||||
⇦ 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. 01–12 #}
|
||||
{% 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'));
|
||||
|
||||
Reference in New Issue
Block a user