This commit is contained in:
2025-10-08 10:13:07 +02:00
parent 70e055d20b
commit b74cf45c5c
10 changed files with 813 additions and 91 deletions

View File

@@ -3,6 +3,9 @@
<head>
<meta charset="UTF-8">
<title>{% block title %}My App{% endblock %}</title>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.css" />
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.js"></script>

View File

@@ -0,0 +1,479 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Betriebskosten</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.css" />
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f4f4f9;
}
.table-container {
width: 100%;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
h2 {
text-align: center;
color: #333;
}
table {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: center;
border-bottom: 1px solid #ddd;
}
th:nth-child(1), td:nth-child(1) { width: 5%; } /* # column */
th:nth-child(2), td:nth-child(2) { width: 10%; } /* Buchungsdatum */
th:nth-child(3), td:nth-child(3) { width: 15%; } /* Rechnungsnummer */
th:nth-child(4), td:nth-child(4) { width: 10%; } /* Kostentyp */
th:nth-child(5), td:nth-child(5) { width: 10%; } /* Gasvolumen */
th:nth-child(6), td:nth-child(6) { width: 10%; } /* Betrag */
th:nth-child(7), td:nth-child(7) { width: 25%; } /* Beschreibung */
th:nth-child(8), td:nth-child(8) { width: 15%; } /* Actions */
.actions {
white-space: nowrap; /* Prevent buttons from wrapping */
}
th {
background-color: #007bff;
color: white;
font-weight: bold;
position: sticky;
top: 0;
}
tr:hover {
background-color: #f1f1f1;
}
.actions button {
margin: 2px;
padding: 5px 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.edit-btn {
background-color: #28a745;
color: white;
}
.delete-btn {
background-color: #dc3545;
color: white;
}
.popup {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
z-index: 1000;
width: 400px;
}
.popup input, .popup select, .popup textarea {
display: block;
margin-bottom: 10px;
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.popup button {
margin-top: 10px;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.close-btn {
cursor: pointer;
float: right;
font-size: 18px;
color: #333;
}
.add-row-btn {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin-bottom: 20px;
}
.add-row-btn:hover {
background-color: #0056b3;
}
.save-btn {
background-color: #28a745;
color: white;
}
.cancel-btn {
background-color: #6c757d;
color: white;
}
.help-btn {
background-color: #17a2b8;
color: white;
}
#price-per-liter-group {
display: none;
}
</style>
</head>
<body>
<a href="{% url 'clients_list' %}" class="btn btn-outline-primary">
&#8678; Go to Clients
</a>
<h2>Betriebskosten</h2>
<div class="table-container">
<button class="add-row-btn" id="add-row-btn">Add Row</button>
<table id="betriebskosten-table">
<thead>
<tr>
<th>#</th>
<th>Buchungsdatum</th>
<th>Rechnungsnummer</th>
<th>Kostentyp</th>
<th>Gasvolumen (L)</th>
<th>Betrag (€)</th>
<th>Beschreibung</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr data-id="{{ item.id }}">
<td>{{ forloop.counter }}</td>
<td>{{ item.buchungsdatum|date:"Y-m-d" }}</td>
<td>{{ item.rechnungsnummer }}</td>
<td>{{ item.get_kostentyp_display }}</td>
<td>{{ item.gas_volume|default_if_none:"-" }}</td>
<td>{{ item.betrag }}</td>
<td>{{ item.beschreibung|default:"" }}</td>
<td class="actions">
<button class="edit-btn">Edit</button>
<button class="delete-btn">Delete</button>
</td>
</tr>
{% empty %}
<tr>
<td colspan="8" style="text-align: center;">No entries found</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Add Popup -->
<div id="add-popup" class="popup">
<span class="close-btn">&times;</span>
<h3>Betriebskosten Eintrag</h3>
<label for="add-buchungsdatum">Buchungsdatum:</label>
<input type="date" id="add-buchungsdatum" required>
<label for="add-rechnungsnummer">Rechnungsnummer:</label>
<input type="text" id="add-rechnungsnummer" required>
<label for="add-kostentyp">Kostentyp:</label>
<select id="add-kostentyp" required onchange="toggleGasVolume('add')">
<option value="">Bitte auswählen</option>
<option value="sach">Sachkosten</option>
<option value="ln2">LN2</option>
<option value="helium">Helium</option>
<option value="inv">Inventar</option>
</select>
<div id="add-gas-volume-group" style="display: none;">
<label for="add-gas-volume">Gasvolumen (Liter):</label>
<input type="number" id="add-gas-volume" step="0.01" min="0" oninput="calculatePrice('add')">
</div>
<label for="add-betrag">Betrag (€):</label>
<input type="number" id="add-betrag" step="0.01" min="0" required oninput="calculatePrice('add')">
<div id="add-price-per-liter-group" style="display: none;">
<label for="add-price-per-liter">Preis pro Liter (€):</label>
<input type="text" id="add-price-per-liter" readonly>
</div>
<label for="add-beschreibung">Beschreibung:</label>
<textarea id="add-beschreibung" placeholder="Optionale Beschreibung"></textarea>
<div class="popup-buttons">
<button class="save-btn" id="save-add">Save</button>
<button class="cancel-btn">Cancel</button>
<button class="help-btn">Help</button>
</div>
</div>
<!-- Edit Popup -->
<div id="edit-popup" class="popup">
<span class="close-btn">&times;</span>
<h3>Betriebskosten Eintrag</h3>
<input type="hidden" id="edit-id">
<label for="edit-buchungsdatum">Buchungsdatum:</label>
<input type="date" id="edit-buchungsdatum" required>
<label for="edit-rechnungsnummer">Rechnungsnummer:</label>
<input type="text" id="edit-rechnungsnummer" required>
<label for="edit-kostentyp">Kostentyp:</label>
<select id="edit-kostentyp" required onchange="toggleGasVolume('edit')">
<option value="">Bitte auswählen</option>
<option value="sach">Sachkosten</option>
<option value="ln2">LN2</option>
<option value="helium">Helium</option>
<option value="inv">Inventar</option>
</select>
<div id="edit-gas-volume-group" style="display: none;">
<label for="edit-gas-volume">Gasvolumen (Liter):</label>
<input type="number" id="edit-gas-volume" step="0.01" min="0" oninput="calculatePrice('edit')">
</div>
<label for="edit-betrag">Betrag (€):</label>
<input type="number" id="edit-betrag" step="0.01" min="0" required oninput="calculatePrice('edit')">
<div id="edit-price-per-liter-group" style="display: none;">
<label for="edit-price-per-liter">Preis pro Liter (€):</label>
<input type="text" id="edit-price-per-liter" readonly>
</div>
<label for="edit-beschreibung">Beschreibung:</label>
<textarea id="edit-beschreibung" placeholder="Optionale Beschreibung"></textarea>
<div class="popup-buttons">
<button class="save-btn" id="save-edit">Save</button>
<button class="cancel-btn">Cancel</button>
<button class="help-btn">Help</button>
</div>
</div>
<script>
$(document).ready(function () {
// Open add popup
$('#add-row-btn').on('click', function () {
$('#add-popup').fadeIn();
});
// Close popups
$('.close-btn, .cancel-btn').on('click', function () {
$('.popup').fadeOut();
});
// Add new entry
$('#save-add').on('click', function() {
const formData = {
'buchungsdatum': $('#add-buchungsdatum').val(),
'rechnungsnummer': $('#add-rechnungsnummer').val(),
'kostentyp': $('#add-kostentyp').val(),
'gas_volume': $('#add-gas-volume').val(),
'betrag': $('#add-betrag').val(),
'beschreibung': $('#add-beschreibung').val(),
'csrfmiddlewaretoken': '{{ csrf_token }}'
};
// Validate required fields
if (!formData.buchungsdatum || !formData.rechnungsnummer || !formData.kostentyp || !formData.betrag) {
alert('Bitte füllen Sie alle erforderlichen Felder aus');
return;
}
$.ajax({
url: "{% url 'betriebskosten_create' %}",
method: 'POST',
data: formData,
success: function(response) {
if (response.status === 'success') {
// Add the new row to the table
const newRow = `
<tr data-id="${response.id}">
<td>${$('#betriebskosten-table tbody tr').length + 1}</td>
<td>${response.buchungsdatum}</td>
<td>${response.rechnungsnummer}</td>
<td>${response.kostentyp_display}</td>
<td>${response.gas_volume || '-'}</td>
<td>${response.betrag}</td>
<td>${response.beschreibung || ''}</td>
<td class="actions">
<button class="edit-btn">Edit</button>
<button class="delete-btn">Delete</button>
</td>
</tr>
`;
$('#betriebskosten-table tbody').append(newRow);
$('#add-popup').fadeOut();
$('#add-popup input, #add-popup select, #add-popup textarea').val('');
} else {
alert('Error: ' + (response.message || 'Failed to add entry'));
}
},
error: function(xhr) {
alert('Error: ' + (xhr.responseJSON?.message || 'Server error'));
}
});
});
// Edit entry
$(document).on('click', '.edit-btn', function() {
const row = $(this).closest('tr');
const id = row.data('id');
// Fill the edit form with current data
$('#edit-id').val(id);
$('#edit-buchungsdatum').val(row.find('td:eq(1)').text());
$('#edit-rechnungsnummer').val(row.find('td:eq(2)').text());
// Set kostentyp based on display text
const kostentypText = row.find('td:eq(3)').text();
const kostentypMap = {
'Sachkosten': 'sach',
'LN2': 'ln2',
'Helium': 'helium',
'Inventar': 'inv'
};
$('#edit-kostentyp').val(kostentypMap[kostentypText] || '');
$('#edit-gas-volume').val(row.find('td:eq(4)').text() === '-' ? '' : row.find('td:eq(4)').text());
$('#edit-betrag').val(row.find('td:eq(5)').text());
$('#edit-beschreibung').val(row.find('td:eq(6)').text());
// Show/hide gas volume based on kostentyp
toggleGasVolume('edit');
calculatePrice('edit');
$('#edit-popup').fadeIn();
});
// Save edit
$('#save-edit').on('click', function() {
const formData = {
'id': $('#edit-id').val(),
'buchungsdatum': $('#edit-buchungsdatum').val(),
'rechnungsnummer': $('#edit-rechnungsnummer').val(),
'kostentyp': $('#edit-kostentyp').val(),
'gas_volume': $('#edit-gas-volume').val(),
'betrag': $('#edit-betrag').val(),
'beschreibung': $('#edit-beschreibung').val(),
'csrfmiddlewaretoken': '{{ csrf_token }}'
};
// Validate required fields
if (!formData.buchungsdatum || !formData.rechnungsnummer || !formData.kostentyp || !formData.betrag) {
alert('Bitte füllen Sie alle erforderlichen Felder aus');
return;
}
$.ajax({
url: "{% url 'betriebskosten_create' %}",
method: 'POST',
data: formData,
success: function(response) {
if (response.status === 'success') {
// Update the row in the table
const row = $(`tr[data-id="${response.id}"]`);
row.find('td:eq(1)').text(response.buchungsdatum);
row.find('td:eq(2)').text(response.rechnungsnummer);
row.find('td:eq(3)').text(response.kostentyp_display);
row.find('td:eq(4)').text(response.gas_volume || '-');
row.find('td:eq(5)').text(response.betrag);
row.find('td:eq(6)').text(response.beschreibung || '');
$('#edit-popup').fadeOut();
} else {
alert('Error: ' + (response.message || 'Failed to update entry'));
}
},
error: function(xhr) {
alert('Error: ' + (xhr.responseJSON?.message || 'Server error'));
}
});
});
// Delete entry
$(document).on('click', '.delete-btn', function () {
const row = $(this).closest('tr');
const id = row.data('id');
if (!confirm('Are you sure you want to delete this entry?')) return;
$.ajax({
url: "{% url 'betriebskosten_delete' %}",
method: 'POST',
data: {
'id': id,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function (response) {
if (response.status === 'success') {
row.fadeOut(300, function () { $(this).remove(); });
// Update row numbers
$('#betriebskosten-table tbody tr').each(function(index) {
$(this).find('td:first').text(index + 1);
});
} else {
alert('Failed to delete entry: ' + response.message);
}
},
error: function () {
alert('Failed to delete entry. Please try again.');
}
});
});
});
// Toggle gas volume field based on kostentyp
function toggleGasVolume(type) {
const kostentyp = $(`#${type}-kostentyp`).val();
const gasVolumeGroup = $(`#${type}-gas-volume-group`);
const pricePerLiterGroup = $(`#${type}-price-per-liter-group`);
if (kostentyp === 'helium') {
gasVolumeGroup.show();
pricePerLiterGroup.show();
} else {
gasVolumeGroup.hide();
pricePerLiterGroup.hide();
$(`#${type}-gas-volume`).val('');
$(`#${type}-price-per-liter`).val('');
}
}
// Calculate price per liter
function calculatePrice(type) {
const kostentyp = $(`#${type}-kostentyp`).val();
const volume = parseFloat($(`#${type}-gas-volume`).val()) || 0;
const betrag = parseFloat($(`#${type}-betrag`).val()) || 0;
if (kostentyp === 'helium' && volume > 0 && betrag > 0) {
$(`#${type}-price-per-liter`).val((betrag / volume).toFixed(2) + ' €/L');
} else {
$(`#${type}-price-per-liter`).val('');
}
}
</script>
</body>
</html>

View File

@@ -2,6 +2,9 @@
{% block content %}
<div class="container">
<!-- Title -->
<h1 class="page-title">Helium Output Yearly Summary</h1>
<!-- Year Filter -->
<div class="year-filter">
<label for="year-select">Year:</label>
@@ -40,12 +43,10 @@
<!-- Navigation Buttons -->
<div class="navigation-buttons">
<a href="{% url 'table_one' %}" class="nav-button">
Go to Helium Input
</a>
<a href="{% url 'table_two' %}" class="nav-button">
Go to Helium Output
</a>
<a href="{% url 'table_one' %}" class="nav-button">Go to Helium Input</a>
<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>
</div>
</div>
@@ -56,6 +57,12 @@
padding: 20px;
}
.page-title {
text-align: center;
color: #333;
margin-bottom: 20px;
}
.year-filter {
margin: 20px 0;
text-align: right;
@@ -120,5 +127,13 @@
.nav-button:hover {
background-color: #0056b3;
}
.admin-button {
background-color: #6c757d;
}
.admin-button:hover {
background-color: #5a6268;
}
</style>
{% endblock %}

View File

@@ -316,7 +316,7 @@
$('#save-add-two').on('click', function() {
let formData = {
'client_id': $('#add-client-id').val(),
'date': $('#add-date').val(),
'date': $('#add-date').val(), // Ensure this is in YYYY-MM-DD format
'is_warm': $('#add-is-warm').is(':checked'),
'lhe_delivery': $('#add-lhe-delivery').val(),
'lhe_output': $('#add-lhe-output').val(),
@@ -324,6 +324,12 @@
'csrfmiddlewaretoken': '{{ csrf_token }}'
};
// Validate date format
if (!formData.date) {
alert('Please select a date');
return;
}
// Validate LHe Output is a number
if (isNaN(parseFloat(formData.lhe_output))) {
alert('LHe Output must be a valid number');
@@ -335,16 +341,32 @@
method: 'POST',
data: formData,
success: function(response) {
// Clear the form
$('#add-popup-two').find('input, textarea, select').val('');
$('#add-is-warm').prop('checked', false);
$('#add-popup-two').fadeOut();
// Reload the table data
loadTableData();
if (response.status === 'success') {
// Add the new row to the table
let newRow = `
<tr data-id="${response.id}">
<td>${$('#table-two tbody tr').length + 1}</td>
<td>${response.id}</td>
<td>${response.client_name}</td>
<td>${response.date || ''}</td>
<td>${response.is_warm ? 'Yes' : 'No'}</td>
<td>${response.lhe_delivery}</td>
<td>${response.lhe_output || ''}</td>
<td>${response.notes || ''}</td>
<td class="actions">
<button class="edit-btn-two">Edit</button>
<button class="delete-btn-two">Delete</button>
</td>
</tr>
`;
$('#table-two tbody').append(newRow);
$('#add-popup-two').fadeOut();
} else {
alert('Error: ' + (response.message || 'Failed to add entry'));
}
},
error: function(xhr) {
alert('Error: ' + xhr.responseJSON?.message || 'Failed to add entry');
alert('Error: ' + (xhr.responseJSON?.message || 'Server error'));
}
});
});
@@ -366,18 +388,22 @@
}
// Open Edit Popup
$(document).on('click', '.edit-btn-two', function () {
$(document).on('click', '.edit-btn-two', function() {
let row = $(this).closest('tr');
currentTableId = row.closest('table').attr('id');
currentModelName = 'SecondTableEntry';
// Get data from the row
let is_warm = row.find('td:eq(4)').text().trim() === 'Yes';
$('#edit-id').val(row.data('id'));
$('#edit-client-id').val(row.find('td:eq(2)').text().trim());
$('#edit-date').val(row.find('td:eq(3)').text().trim());
$('#edit-is-warm').prop('checked', is_warm);
// Set client - find by name since we're showing names in the table
let clientName = row.find('td:eq(2)').text().trim();
$(`#edit-client-id option:contains("${clientName}")`).prop('selected', true);
// Set date - ensure proper format
let dateText = row.find('td:eq(3)').text().trim();
if (dateText) {
$('#edit-date').val(dateText);
}
// Set other fields
$('#edit-is-warm').prop('checked', row.find('td:eq(4)').text().trim() === 'Yes');
$('#edit-lhe-delivery').val(row.find('td:eq(5)').text().trim());
$('#edit-lhe-output').val(row.find('td:eq(6)').text().trim());
$('#edit-notes').val(row.find('td:eq(7)').text().trim());
@@ -386,45 +412,48 @@
});
// Save Edit Entry
$('#save-edit-two').on('click', function () {
let id = $('#edit-id').val();
let client_id = $('#edit-client-id').val();
let date = $('#edit-date').val();
let is_warm = $('#edit-is-warm').is(':checked');
let lhe_delivery = $('#edit-lhe-delivery').val();
let lhe_output = $('#edit-lhe-output').val();
let notes = $('#edit-notes').val();
$('#save-edit-two').on('click', function() {
let formData = {
'id': $('#edit-id').val(),
'client_id': $('#edit-client-id').val(),
'date': $('#edit-date').val(), // Already in YYYY-MM-DD format
'is_warm': $('#edit-is-warm').is(':checked'),
'lhe_delivery': $('#edit-lhe-delivery').val(),
'lhe_output': $('#edit-lhe-output').val(),
'notes': $('#edit-notes').val(),
'csrfmiddlewaretoken': '{{ csrf_token }}'
};
// Validate inputs
if (!formData.date) {
alert('Please select a date');
return;
}
if (isNaN(parseFloat(formData.lhe_output))) {
alert('Please enter a valid LHe Output value');
return;
}
$.ajax({
url: `/update-entry/${currentModelName}/`,
url: '/update-entry/SecondTableEntry/',
method: 'POST',
data: {
'id': id,
'client_id': client_id,
'date': date,
'is_warm': is_warm,
'lhe_delivery': lhe_delivery,
'lhe_output': lhe_output,
'notes': notes,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function (response) {
data: formData,
success: function(response) {
if (response.status === 'success') {
let row = $(`tr[data-id="${response.id}"]`);
row.find('td:eq(2)').text(response.client_name);
row.find('td:eq(3)').text(response.date);
row.find('td:eq(3)').text(response.date || '');
row.find('td:eq(4)').text(response.is_warm ? 'Yes' : 'No');
row.find('td:eq(5)').text(response.lhe_delivery);
row.find('td:eq(6)').text(response.lhe_output);
row.find('td:eq(7)').text(response.notes);
row.find('td:eq(6)').text(response.lhe_output || '');
row.find('td:eq(7)').text(response.notes || '');
$('#edit-popup-two').fadeOut();
} else {
alert('Update failed: ' + (response.message || 'Please try again later'));
alert('Update failed: ' + (response.message || 'Unknown error'));
}
},
error: function (xhr, status, error) {
alert('Update failed: ' + (xhr.responseJSON?.message || 'Please try again later'));
console.error('Error:', error);
error: function(xhr) {
alert('Error: ' + (xhr.responseJSON?.message || 'Server error'));
}
});
});