Ablaufdatum-Berechnung

Dieser Commit ist enthalten in:
2025-06-08 00:00:33 +02:00
Ursprung cba8c953ec
Commit 0c08147af2
5 geänderte Dateien mit 175 neuen und 15 gelöschten Zeilen

Datei anzeigen

@@ -1063,4 +1063,35 @@ Die Session-Daten werden erst gefüllt, wenn der License Server API implementier
- ✅ Select2 Dropdown mit Suchfunktion
- ✅ Neue/bestehende Kunden können ausgewählt werden
- ✅ E-Mail-Duplikate werden verhindert
- ✅ Sowohl Einzellizenz als auch Batch unterstützt
- ✅ Sowohl Einzellizenz als auch Batch unterstützt
## 2025-01-06: Automatische Ablaufdatum-Berechnung
**Problem:**
- Manuelles Eingeben von Start- und Enddatum war umständlich
- Fehleranfällig bei der Datumseingabe
- Nicht intuitiv für Standard-Laufzeiten
**Lösung:**
1. **Frontend-Änderungen:**
- Startdatum + Laufzeit (Zahl) + Einheit (Tage/Monate/Jahre)
- Ablaufdatum wird automatisch berechnet und angezeigt (read-only)
- Standard: 1 Jahr Laufzeit voreingestellt
2. **Backend-Validierung:**
- Server-seitige Berechnung zur Sicherheit
- Verwendung von `python-dateutil` für korrekte Monats-/Jahresberechnungen
3. **Benutzerfreundlichkeit:**
- Sofortige Neuberechnung bei Änderungen
- Visuelle Hervorhebung des berechneten Datums
**Änderungen:**
- `index.html`: Laufzeit-Eingabe statt Ablaufdatum
- `batch_form.html`: Laufzeit-Eingabe statt Ablaufdatum
- `app.py`: Datum-Berechnung in `/create` und `/batch` Routes
- `requirements.txt`: `python-dateutil` hinzugefügt
**Status:**
- ✅ Automatische Berechnung funktioniert
- ✅ Frontend zeigt berechnetes Datum sofort an
- ✅ Backend validiert die Berechnung
- ✅ Standardwert (1 Jahr) voreingestellt

Datei anzeigen

@@ -1015,7 +1015,26 @@ def create_license():
license_key = request.form["license_key"].upper() # Immer Großbuchstaben
license_type = request.form["license_type"]
valid_from = request.form["valid_from"]
valid_until = request.form["valid_until"]
# Berechne valid_until basierend auf Laufzeit
duration = int(request.form.get("duration", 1))
duration_type = request.form.get("duration_type", "years")
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
start_date = datetime.strptime(valid_from, "%Y-%m-%d")
if duration_type == "days":
end_date = start_date + timedelta(days=duration)
elif duration_type == "months":
end_date = start_date + relativedelta(months=duration)
else: # years
end_date = start_date + relativedelta(years=duration)
# Ein Tag abziehen, da der Starttag mitgezählt wird
end_date = end_date - timedelta(days=1)
valid_until = end_date.strftime("%Y-%m-%d")
# Validiere License Key Format
if not validate_license_key(license_key):
@@ -1110,7 +1129,26 @@ def batch_licenses():
license_type = request.form["license_type"]
quantity = int(request.form["quantity"])
valid_from = request.form["valid_from"]
valid_until = request.form["valid_until"]
# Berechne valid_until basierend auf Laufzeit
duration = int(request.form.get("duration", 1))
duration_type = request.form.get("duration_type", "years")
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
start_date = datetime.strptime(valid_from, "%Y-%m-%d")
if duration_type == "days":
end_date = start_date + timedelta(days=duration)
elif duration_type == "months":
end_date = start_date + relativedelta(months=duration)
else: # years
end_date = start_date + relativedelta(years=duration)
# Ein Tag abziehen, da der Starttag mitgezählt wird
end_date = end_date - timedelta(days=1)
valid_until = end_date.strftime("%Y-%m-%d")
# Sicherheitslimit
if quantity < 1 or quantity > 100:

Datei anzeigen

@@ -8,3 +8,4 @@ openpyxl
cryptography
apscheduler
requests
python-dateutil

Datei anzeigen

@@ -66,14 +66,28 @@
</select>
</div>
<div class="col-md-3">
<div class="col-md-2">
<label for="validFrom" class="form-label">Gültig ab</label>
<input type="date" class="form-control" id="validFrom" name="valid_from" required>
</div>
<div class="col-md-3">
<div class="col-md-1">
<label for="duration" class="form-label">Laufzeit</label>
<input type="number" class="form-control" id="duration" name="duration" value="1" min="1" required>
</div>
<div class="col-md-2">
<label for="durationType" class="form-label">Einheit</label>
<select class="form-select" id="durationType" name="duration_type" required>
<option value="days">Tage</option>
<option value="months">Monate</option>
<option value="years" selected>Jahre</option>
</select>
</div>
<div class="col-md-2">
<label for="validUntil" class="form-label">Gültig bis</label>
<input type="date" class="form-control" id="validUntil" name="valid_until" required>
<input type="date" class="form-control" id="validUntil" name="valid_until" readonly style="background-color: #e9ecef;">
</div>
</div>
@@ -111,15 +125,47 @@
</div>
<script>
// Funktion zur Berechnung des Ablaufdatums
function calculateValidUntil() {
const validFrom = document.getElementById('validFrom').value;
const duration = parseInt(document.getElementById('duration').value) || 1;
const durationType = document.getElementById('durationType').value;
if (!validFrom) return;
const startDate = new Date(validFrom);
let endDate = new Date(startDate);
switch(durationType) {
case 'days':
endDate.setDate(endDate.getDate() + duration);
break;
case 'months':
endDate.setMonth(endDate.getMonth() + duration);
break;
case 'years':
endDate.setFullYear(endDate.getFullYear() + duration);
break;
}
// Ein Tag abziehen, da der Starttag mitgezählt wird
endDate.setDate(endDate.getDate() - 1);
document.getElementById('validUntil').value = endDate.toISOString().split('T')[0];
}
// Event Listener für Änderungen
document.getElementById('validFrom').addEventListener('change', calculateValidUntil);
document.getElementById('duration').addEventListener('input', calculateValidUntil);
document.getElementById('durationType').addEventListener('change', calculateValidUntil);
// Setze heutiges Datum als Standard
document.addEventListener('DOMContentLoaded', function() {
const today = new Date().toISOString().split('T')[0];
document.getElementById('validFrom').value = today;
// Setze valid_until auf 1 Jahr später
const oneYearLater = new Date();
oneYearLater.setFullYear(oneYearLater.getFullYear() + 1);
document.getElementById('validUntil').value = oneYearLater.toISOString().split('T')[0];
// Berechne initiales Ablaufdatum
calculateValidUntil();
// Initialisiere Select2 für Kundenauswahl
$('#customerSelect').select2({

Datei anzeigen

@@ -58,9 +58,21 @@
<label for="validFrom" class="form-label">Kaufdatum</label>
<input type="date" class="form-control" id="validFrom" name="valid_from" required>
</div>
<div class="col-md-1">
<label for="duration" class="form-label">Laufzeit</label>
<input type="number" class="form-control" id="duration" name="duration" value="1" min="1" required>
</div>
<div class="col-md-1">
<label for="durationType" class="form-label">Einheit</label>
<select class="form-select" id="durationType" name="duration_type" required>
<option value="days">Tage</option>
<option value="months">Monate</option>
<option value="years" selected>Jahre</option>
</select>
</div>
<div class="col-md-2">
<label for="validUntil" class="form-label">Ablaufdatum</label>
<input type="date" class="form-control" id="validUntil" name="valid_until" required>
<input type="date" class="form-control" id="validUntil" name="valid_until" readonly style="background-color: #e9ecef;">
</div>
</div>
@@ -153,15 +165,47 @@ document.getElementById('licenseKey').addEventListener('input', function(e) {
e.target.value = e.target.value.toUpperCase();
});
// Funktion zur Berechnung des Ablaufdatums
function calculateValidUntil() {
const validFrom = document.getElementById('validFrom').value;
const duration = parseInt(document.getElementById('duration').value) || 1;
const durationType = document.getElementById('durationType').value;
if (!validFrom) return;
const startDate = new Date(validFrom);
let endDate = new Date(startDate);
switch(durationType) {
case 'days':
endDate.setDate(endDate.getDate() + duration);
break;
case 'months':
endDate.setMonth(endDate.getMonth() + duration);
break;
case 'years':
endDate.setFullYear(endDate.getFullYear() + duration);
break;
}
// Ein Tag abziehen, da der Starttag mitgezählt wird
endDate.setDate(endDate.getDate() - 1);
document.getElementById('validUntil').value = endDate.toISOString().split('T')[0];
}
// Event Listener für Änderungen
document.getElementById('validFrom').addEventListener('change', calculateValidUntil);
document.getElementById('duration').addEventListener('input', calculateValidUntil);
document.getElementById('durationType').addEventListener('change', calculateValidUntil);
// Setze heutiges Datum als Standard für valid_from
document.addEventListener('DOMContentLoaded', function() {
const today = new Date().toISOString().split('T')[0];
document.getElementById('validFrom').value = today;
// Setze valid_until auf 1 Jahr später als Standard
const oneYearLater = new Date();
oneYearLater.setFullYear(oneYearLater.getFullYear() + 1);
document.getElementById('validUntil').value = oneYearLater.toISOString().split('T')[0];
// Berechne initiales Ablaufdatum
calculateValidUntil();
// Initialisiere Select2 für Kundenauswahl
$('#customerSelect').select2({