Test zu Fake geändert, weil Namensproblem

Dieser Commit ist enthalten in:
2025-06-21 17:22:12 +02:00
Ursprung fec588ba06
Commit 3d899b1c45
22 geänderte Dateien mit 614 neuen und 347 gelöschten Zeilen

Datei anzeigen

@@ -73,7 +73,8 @@
"Bash(docker compose:*)", "Bash(docker compose:*)",
"Bash(true)", "Bash(true)",
"Bash(git checkout:*)", "Bash(git checkout:*)",
"Bash(touch:*)" "Bash(touch:*)",
"Bash(wget:*)"
], ],
"deny": [] "deny": []
} }

Datei anzeigen

@@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS customers (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
email TEXT, email TEXT,
is_test BOOLEAN DEFAULT FALSE, is_fake BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_email UNIQUE (email) CONSTRAINT unique_email UNIQUE (email)
); );
@@ -21,7 +21,7 @@ CREATE TABLE IF NOT EXISTS licenses (
valid_from DATE NOT NULL, valid_from DATE NOT NULL,
valid_until DATE NOT NULL, valid_until DATE NOT NULL,
is_active BOOLEAN DEFAULT TRUE, is_active BOOLEAN DEFAULT TRUE,
is_test BOOLEAN DEFAULT FALSE, is_fake BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
); );
@@ -129,7 +129,7 @@ CREATE TABLE IF NOT EXISTS resource_pools (
quarantine_until TIMESTAMP WITH TIME ZONE, quarantine_until TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
notes TEXT, notes TEXT,
is_test BOOLEAN DEFAULT FALSE, is_fake BOOLEAN DEFAULT FALSE,
UNIQUE(resource_type, resource_value) UNIQUE(resource_type, resource_value)
); );
@@ -245,48 +245,48 @@ CREATE TABLE IF NOT EXISTS users (
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
CREATE INDEX IF NOT EXISTS idx_users_reset_token ON users(password_reset_token) WHERE password_reset_token IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_users_reset_token ON users(password_reset_token) WHERE password_reset_token IS NOT NULL;
-- Migration: Add is_test column to licenses if it doesn't exist -- Migration: Add is_fake column to licenses if it doesn't exist
DO $$ DO $$
BEGIN BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns IF NOT EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'licenses' AND column_name = 'is_test') THEN WHERE table_name = 'licenses' AND column_name = 'is_fake') THEN
ALTER TABLE licenses ADD COLUMN is_test BOOLEAN DEFAULT FALSE; ALTER TABLE licenses ADD COLUMN is_fake BOOLEAN DEFAULT FALSE;
-- Mark all existing licenses as test data -- Mark all existing licenses as fake data
UPDATE licenses SET is_test = TRUE; UPDATE licenses SET is_fake = TRUE;
-- Add index for better performance when filtering test data -- Add index for better performance when filtering fake data
CREATE INDEX idx_licenses_is_test ON licenses(is_test); CREATE INDEX idx_licenses_is_fake ON licenses(is_fake);
END IF; END IF;
END $$; END $$;
-- Migration: Add is_test column to customers if it doesn't exist -- Migration: Add is_fake column to customers if it doesn't exist
DO $$ DO $$
BEGIN BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns IF NOT EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'customers' AND column_name = 'is_test') THEN WHERE table_name = 'customers' AND column_name = 'is_fake') THEN
ALTER TABLE customers ADD COLUMN is_test BOOLEAN DEFAULT FALSE; ALTER TABLE customers ADD COLUMN is_fake BOOLEAN DEFAULT FALSE;
-- Mark all existing customers as test data -- Mark all existing customers as fake data
UPDATE customers SET is_test = TRUE; UPDATE customers SET is_fake = TRUE;
-- Add index for better performance -- Add index for better performance
CREATE INDEX idx_customers_is_test ON customers(is_test); CREATE INDEX idx_customers_is_fake ON customers(is_fake);
END IF; END IF;
END $$; END $$;
-- Migration: Add is_test column to resource_pools if it doesn't exist -- Migration: Add is_fake column to resource_pools if it doesn't exist
DO $$ DO $$
BEGIN BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns IF NOT EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'resource_pools' AND column_name = 'is_test') THEN WHERE table_name = 'resource_pools' AND column_name = 'is_fake') THEN
ALTER TABLE resource_pools ADD COLUMN is_test BOOLEAN DEFAULT FALSE; ALTER TABLE resource_pools ADD COLUMN is_fake BOOLEAN DEFAULT FALSE;
-- Mark all existing resources as test data -- Mark all existing resources as fake data
UPDATE resource_pools SET is_test = TRUE; UPDATE resource_pools SET is_fake = TRUE;
-- Add index for better performance -- Add index for better performance
CREATE INDEX idx_resource_pools_is_test ON resource_pools(is_test); CREATE INDEX idx_resource_pools_is_fake ON resource_pools(is_fake);
END IF; END IF;
END $$; END $$;

Datei anzeigen

@@ -0,0 +1,48 @@
-- Migration script to rename is_test columns to is_fake
-- This separates fake/demo data from test licenses
-- 1. Rename columns in all tables
DO $$
BEGIN
-- Rename is_test to is_fake in customers table
IF EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'customers' AND column_name = 'is_test') THEN
ALTER TABLE customers RENAME COLUMN is_test TO is_fake;
END IF;
-- Rename is_test to is_fake in licenses table
IF EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'licenses' AND column_name = 'is_test') THEN
ALTER TABLE licenses RENAME COLUMN is_test TO is_fake;
END IF;
-- Rename is_test to is_fake in resource_pools table
IF EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'resource_pools' AND column_name = 'is_test') THEN
ALTER TABLE resource_pools RENAME COLUMN is_test TO is_fake;
END IF;
END $$;
-- 2. Rename indexes
DO $$
BEGIN
-- Rename index for customers
IF EXISTS (SELECT 1 FROM pg_indexes WHERE indexname = 'idx_customers_is_test') THEN
ALTER INDEX idx_customers_is_test RENAME TO idx_customers_is_fake;
END IF;
-- Rename index for licenses
IF EXISTS (SELECT 1 FROM pg_indexes WHERE indexname = 'idx_licenses_is_test') THEN
ALTER INDEX idx_licenses_is_test RENAME TO idx_licenses_is_fake;
END IF;
-- Rename index for resource_pools
IF EXISTS (SELECT 1 FROM pg_indexes WHERE indexname = 'idx_resource_pools_is_test') THEN
ALTER INDEX idx_resource_pools_is_test RENAME TO idx_resource_pools_is_fake;
END IF;
END $$;
-- 3. Add comments to clarify the purpose
COMMENT ON COLUMN customers.is_fake IS 'Marks fake/demo data, not to be confused with test licenses';
COMMENT ON COLUMN licenses.is_fake IS 'Marks fake/demo data, not to be confused with test license type';
COMMENT ON COLUMN resource_pools.is_fake IS 'Marks fake/demo resources';

Datei anzeigen

@@ -32,12 +32,12 @@ def get_user_by_username(username):
return None return None
def get_licenses(show_test=False): def get_licenses(show_fake=False):
"""Get all licenses from database""" """Get all licenses from database"""
try: try:
with get_db_connection() as conn: with get_db_connection() as conn:
with get_db_cursor(conn) as cur: with get_db_cursor(conn) as cur:
if show_test: if show_fake:
cur.execute(""" cur.execute("""
SELECT l.*, c.name as customer_name SELECT l.*, c.name as customer_name
FROM licenses l FROM licenses l
@@ -49,7 +49,7 @@ def get_licenses(show_test=False):
SELECT l.*, c.name as customer_name SELECT l.*, c.name as customer_name
FROM licenses l FROM licenses l
LEFT JOIN customers c ON l.customer_id = c.id LEFT JOIN customers c ON l.customer_id = c.id
WHERE l.is_test = false WHERE l.is_fake = false
ORDER BY l.created_at DESC ORDER BY l.created_at DESC
""") """)
@@ -86,7 +86,7 @@ def get_license_by_id(license_id):
return None return None
def get_customers(show_test=False, search=None): def get_customers(show_fake=False, search=None):
"""Get all customers from database""" """Get all customers from database"""
try: try:
with get_db_connection() as conn: with get_db_connection() as conn:
@@ -102,8 +102,8 @@ def get_customers(show_test=False, search=None):
where_clauses = [] where_clauses = []
params = [] params = []
if not show_test: if not show_fake:
where_clauses.append("c.is_test = false") where_clauses.append("c.is_fake = false")
if search: if search:
where_clauses.append("(LOWER(c.name) LIKE LOWER(%s) OR LOWER(c.email) LIKE LOWER(%s))") where_clauses.append("(LOWER(c.name) LIKE LOWER(%s) OR LOWER(c.email) LIKE LOWER(%s))")

Datei anzeigen

@@ -25,18 +25,18 @@ def dashboard():
try: try:
# Hole Statistiken mit sicheren Defaults # Hole Statistiken mit sicheren Defaults
# Anzahl aktiver Lizenzen (nur echte Daten, keine Testdaten) # Anzahl aktiver Lizenzen (nur echte Daten, keine Testdaten)
cur.execute("SELECT COUNT(*) FROM licenses WHERE is_active = true AND is_test = false") cur.execute("SELECT COUNT(*) FROM licenses WHERE is_active = true AND is_fake = false")
active_licenses = cur.fetchone()[0] if cur.rowcount > 0 else 0 active_licenses = cur.fetchone()[0] if cur.rowcount > 0 else 0
# Anzahl Kunden (nur echte Kunden, keine Testkunden) # Anzahl Kunden (nur echte Kunden, keine Fake-Kunden)
cur.execute("SELECT COUNT(*) FROM customers WHERE is_test = false") cur.execute("SELECT COUNT(*) FROM customers WHERE is_fake = false")
total_customers = cur.fetchone()[0] if cur.rowcount > 0 else 0 total_customers = cur.fetchone()[0] if cur.rowcount > 0 else 0
# Testdaten separat zählen für optionale Anzeige # Testdaten separat zählen für optionale Anzeige
cur.execute("SELECT COUNT(*) FROM customers WHERE is_test = true") cur.execute("SELECT COUNT(*) FROM customers WHERE is_fake = true")
test_customers_count = cur.fetchone()[0] if cur.rowcount > 0 else 0 fake_customers_count = cur.fetchone()[0] if cur.rowcount > 0 else 0
cur.execute("SELECT COUNT(*) FROM licenses WHERE is_test = true") cur.execute("SELECT COUNT(*) FROM licenses WHERE is_fake = true")
test_licenses_count = cur.fetchone()[0] if cur.rowcount > 0 else 0 test_licenses_count = cur.fetchone()[0] if cur.rowcount > 0 else 0
# Anzahl aktiver Sessions (Admin-Panel) # Anzahl aktiver Sessions (Admin-Panel)
@@ -61,7 +61,7 @@ def dashboard():
FROM license_heartbeats lh FROM license_heartbeats lh
JOIN licenses l ON l.id = lh.license_id JOIN licenses l ON l.id = lh.license_id
WHERE lh.timestamp > NOW() - INTERVAL '15 minutes' WHERE lh.timestamp > NOW() - INTERVAL '15 minutes'
AND l.is_test = false AND l.is_fake = false
""") """)
active_usage = cur.fetchone()[0] if cur.rowcount > 0 else 0 active_usage = cur.fetchone()[0] if cur.rowcount > 0 else 0
except Exception as e: except Exception as e:
@@ -82,7 +82,7 @@ def dashboard():
FROM licenses l FROM licenses l
LEFT JOIN customers c ON l.customer_id = c.id LEFT JOIN customers c ON l.customer_id = c.id
LEFT JOIN sessions s ON l.id = s.license_id LEFT JOIN sessions s ON l.id = s.license_id
WHERE l.is_test = false AND c.is_test = false WHERE l.is_fake = false AND c.is_fake = false
GROUP BY l.license_key, c.name GROUP BY l.license_key, c.name
ORDER BY session_count DESC ORDER BY session_count DESC
LIMIT 10 LIMIT 10
@@ -109,7 +109,7 @@ def dashboard():
cur.execute(""" cur.execute("""
SELECT COUNT(*) as full_licenses SELECT COUNT(*) as full_licenses
FROM licenses FROM licenses
WHERE is_test = false WHERE is_fake = false
""") """)
license_count = cur.fetchone() license_count = cur.fetchone()
full_licenses = license_count[0] if license_count and license_count[0] else 0 full_licenses = license_count[0] if license_count and license_count[0] else 0
@@ -123,7 +123,7 @@ def dashboard():
COUNT(CASE WHEN valid_until < NOW() THEN 1 END) as expired, COUNT(CASE WHEN valid_until < NOW() THEN 1 END) as expired,
COUNT(CASE WHEN is_active = false THEN 1 END) as inactive COUNT(CASE WHEN is_active = false THEN 1 END) as inactive
FROM licenses FROM licenses
WHERE is_test = false WHERE is_fake = false
""") """)
license_status = cur.fetchone() license_status = cur.fetchone()
active_licenses_count = license_status[0] if license_status and license_status[0] else 0 active_licenses_count = license_status[0] if license_status and license_status[0] else 0
@@ -140,8 +140,8 @@ def dashboard():
EXTRACT(DAY FROM (l.valid_until - NOW())) as days_remaining EXTRACT(DAY FROM (l.valid_until - NOW())) as days_remaining
FROM licenses l FROM licenses l
JOIN customers c ON l.customer_id = c.id JOIN customers c ON l.customer_id = c.id
WHERE l.is_test = false WHERE l.is_fake = false
AND c.is_test = false AND c.is_fake = false
AND l.is_active = true AND l.is_active = true
AND l.valid_until IS NOT NULL AND l.valid_until IS NOT NULL
AND l.valid_until > NOW() AND l.valid_until > NOW()
@@ -166,8 +166,8 @@ def dashboard():
END as status END as status
FROM licenses l FROM licenses l
JOIN customers c ON l.customer_id = c.id JOIN customers c ON l.customer_id = c.id
WHERE l.is_test = false WHERE l.is_fake = false
AND c.is_test = false AND c.is_fake = false
ORDER BY l.created_at DESC ORDER BY l.created_at DESC
LIMIT 5 LIMIT 5
""") """)
@@ -176,15 +176,15 @@ def dashboard():
# Stats Objekt für Template erstellen # Stats Objekt für Template erstellen
stats = { stats = {
'total_customers': total_customers, 'total_customers': total_customers,
'total_licenses': active_licenses, # This was already filtered for is_test = false 'total_licenses': active_licenses, # This was already filtered for is_fake = false
'active_sessions': active_sessions, # Admin-Panel Sessions 'active_sessions': active_sessions, # Admin-Panel Sessions
'active_usage': active_usage, # Aktive Kunden-Nutzung 'active_usage': active_usage, # Aktive Kunden-Nutzung
'active_licenses': active_licenses_count, 'active_licenses': active_licenses_count,
'full_licenses': full_licenses, 'full_licenses': full_licenses,
'test_licenses': test_version_licenses, # Test versions, not test data 'test_licenses': test_version_licenses, # Test versions, not test data
'test_data_count': test_licenses_count, # Actual test data count 'fake_data_count': test_licenses_count, # Actual test data count
'test_customers_count': test_customers_count, 'fake_customers_count': fake_customers_count,
'test_resources_count': 0, 'fake_resources_count': 0,
'expired_licenses': expired_licenses, 'expired_licenses': expired_licenses,
'inactive_licenses': inactive_licenses, 'inactive_licenses': inactive_licenses,
'last_backup': None, 'last_backup': None,
@@ -210,7 +210,7 @@ def dashboard():
COUNT(CASE WHEN status = 'quarantine' THEN 1 END) as quarantine, COUNT(CASE WHEN status = 'quarantine' THEN 1 END) as quarantine,
COUNT(*) as total COUNT(*) as total
FROM resource_pool FROM resource_pool
WHERE type = %s AND is_test = false WHERE type = %s AND is_fake = false
""", (resource_type,)) """, (resource_type,))
result = cur.fetchone() result = cur.fetchone()
@@ -249,9 +249,9 @@ def dashboard():
# Count test resources separately # Count test resources separately
try: try:
cur.execute("SELECT COUNT(*) FROM resource_pool WHERE is_test = true") cur.execute("SELECT COUNT(*) FROM resource_pool WHERE is_fake = true")
test_resources_count = cur.fetchone()[0] if cur.rowcount > 0 else 0 fake_resources_count = cur.fetchone()[0] if cur.rowcount > 0 else 0
stats['test_resources_count'] = test_resources_count stats['fake_resources_count'] = fake_resources_count
except: except:
pass pass

Datei anzeigen

@@ -25,7 +25,7 @@ def api_customers():
try: try:
# Get all customers (with optional search) # Get all customers (with optional search)
customers = get_customers(show_test=True, search=search) customers = get_customers(show_fake=True, search=search)
# Pagination # Pagination
start = (page - 1) * per_page start = (page - 1) * per_page
@@ -396,7 +396,7 @@ def bulk_delete_licenses():
for license_id in license_ids: for license_id in license_ids:
# Hole vollständige Lizenz-Info # Hole vollständige Lizenz-Info
cur.execute(""" cur.execute("""
SELECT l.id, l.license_key, l.is_active, l.is_test, SELECT l.id, l.license_key, l.is_active, l.is_fake,
c.name as customer_name c.name as customer_name
FROM licenses l FROM licenses l
LEFT JOIN customers c ON l.customer_id = c.id LEFT JOIN customers c ON l.customer_id = c.id
@@ -407,7 +407,7 @@ def bulk_delete_licenses():
if not result: if not result:
continue continue
license_id, license_key, is_active, is_test, customer_name = result license_id, license_key, is_active, is_fake, customer_name = result
# Safety check: Don't delete active licenses unless forced # Safety check: Don't delete active licenses unless forced
if is_active and not force_delete: if is_active and not force_delete:
@@ -601,7 +601,7 @@ def get_license_resources(license_id):
rp.id, rp.id,
rp.resource_type, rp.resource_type,
rp.resource_value, rp.resource_value,
rp.is_test, rp.is_fake,
rp.status_changed_at, rp.status_changed_at,
lr.assigned_at, lr.assigned_at,
lr.assigned_by lr.assigned_by
@@ -617,7 +617,7 @@ def get_license_resources(license_id):
'id': row[0], 'id': row[0],
'type': row[1], 'type': row[1],
'value': row[2], 'value': row[2],
'is_test': row[3], 'is_fake': row[3],
'status_changed_at': row[4].isoformat() if row[4] else None, 'status_changed_at': row[4].isoformat() if row[4] else None,
'assigned_at': row[5].isoformat() if row[5] else None, 'assigned_at': row[5].isoformat() if row[5] else None,
'assigned_by': row[6] 'assigned_by': row[6]
@@ -675,7 +675,7 @@ def allocate_resources():
try: try:
# Prüfe ob Ressource verfügbar ist # Prüfe ob Ressource verfügbar ist
cur.execute(""" cur.execute("""
SELECT resource_value, status, is_test SELECT resource_value, status, is_fake
FROM resource_pools FROM resource_pools
WHERE id = %s WHERE id = %s
""", (resource_id,)) """, (resource_id,))
@@ -690,8 +690,8 @@ def allocate_resources():
continue continue
# Prüfe Test/Produktion Kompatibilität # Prüfe Test/Produktion Kompatibilität
if resource[2] != license_data['is_test']: if resource[2] != license_data['is_fake']:
errors.append(f"Ressource {resource[0]} ist {'Test' if resource[2] else 'Produktion'}, Lizenz ist {'Test' if license_data['is_test'] else 'Produktion'}") errors.append(f"Ressource {resource[0]} ist {'Test' if resource[2] else 'Produktion'}, Lizenz ist {'Test' if license_data['is_fake'] else 'Produktion'}")
continue continue
# Weise Ressource zu # Weise Ressource zu
@@ -751,32 +751,32 @@ def check_resource_availability():
resource_type = request.args.get('type') resource_type = request.args.get('type')
if resource_type: if resource_type:
count = int(request.args.get('count', 1)) count = int(request.args.get('count', 1))
is_test = request.args.get('is_test', 'false') == 'true' is_fake = request.args.get('is_fake', 'false') == 'true'
show_test = request.args.get('show_test', 'false') == 'true' show_fake = request.args.get('show_fake', 'false') == 'true'
conn = get_connection() conn = get_connection()
cur = conn.cursor() cur = conn.cursor()
try: try:
# Hole verfügbare Ressourcen mit Details # Hole verfügbare Ressourcen mit Details
if show_test: if show_fake:
# Zeige alle verfügbaren Ressourcen (Test und Produktion) # Zeige alle verfügbaren Ressourcen (Test und Produktion)
cur.execute(""" cur.execute("""
SELECT id, resource_value, is_test SELECT id, resource_value, is_fake
FROM resource_pools FROM resource_pools
WHERE resource_type = %s WHERE resource_type = %s
AND status = 'available' AND status = 'available'
ORDER BY is_test, resource_value ORDER BY is_fake, resource_value
LIMIT %s LIMIT %s
""", (resource_type, count)) """, (resource_type, count))
else: else:
# Zeige nur Produktions-Ressourcen # Zeige nur Produktions-Ressourcen
cur.execute(""" cur.execute("""
SELECT id, resource_value, is_test SELECT id, resource_value, is_fake
FROM resource_pools FROM resource_pools
WHERE resource_type = %s WHERE resource_type = %s
AND status = 'available' AND status = 'available'
AND is_test = false AND is_fake = false
ORDER BY resource_value ORDER BY resource_value
LIMIT %s LIMIT %s
""", (resource_type, count)) """, (resource_type, count))
@@ -786,7 +786,7 @@ def check_resource_availability():
available_resources.append({ available_resources.append({
'id': row[0], 'id': row[0],
'value': row[1], 'value': row[1],
'is_test': row[2] 'is_fake': row[2]
}) })
return jsonify({ return jsonify({
@@ -794,7 +794,7 @@ def check_resource_availability():
'requested': count, 'requested': count,
'available': available_resources, 'available': available_resources,
'sufficient': len(available_resources) >= count, 'sufficient': len(available_resources) >= count,
'show_test': show_test 'show_fake': show_fake
}) })
except Exception as e: except Exception as e:
@@ -808,7 +808,7 @@ def check_resource_availability():
domain_count = int(request.args.get('domain', 0)) domain_count = int(request.args.get('domain', 0))
ipv4_count = int(request.args.get('ipv4', 0)) ipv4_count = int(request.args.get('ipv4', 0))
phone_count = int(request.args.get('phone', 0)) phone_count = int(request.args.get('phone', 0))
is_test = request.args.get('is_test', 'false') == 'true' is_fake = request.args.get('is_fake', 'false') == 'true'
conn = get_connection() conn = get_connection()
cur = conn.cursor() cur = conn.cursor()
@@ -823,8 +823,8 @@ def check_resource_availability():
FROM resource_pools FROM resource_pools
WHERE resource_type = 'domain' WHERE resource_type = 'domain'
AND status = 'available' AND status = 'available'
AND is_test = %s AND is_fake = %s
""", (is_test,)) """, (is_fake,))
domain_available = cur.fetchone()[0] domain_available = cur.fetchone()[0]
# IPv4 # IPv4
@@ -833,8 +833,8 @@ def check_resource_availability():
FROM resource_pools FROM resource_pools
WHERE resource_type = 'ipv4' WHERE resource_type = 'ipv4'
AND status = 'available' AND status = 'available'
AND is_test = %s AND is_fake = %s
""", (is_test,)) """, (is_fake,))
ipv4_available = cur.fetchone()[0] ipv4_available = cur.fetchone()[0]
# Phones # Phones
@@ -843,8 +843,8 @@ def check_resource_availability():
FROM resource_pools FROM resource_pools
WHERE resource_type = 'phone' WHERE resource_type = 'phone'
AND status = 'available' AND status = 'available'
AND is_test = %s AND is_fake = %s
""", (is_test,)) """, (is_fake,))
phone_available = cur.fetchone()[0] phone_available = cur.fetchone()[0]
return jsonify({ return jsonify({
@@ -862,7 +862,7 @@ def check_resource_availability():
ipv4_available >= ipv4_count and ipv4_available >= ipv4_count and
phone_available >= phone_count phone_available >= phone_count
), ),
'is_test': is_test 'is_fake': is_fake
}) })
except Exception as e: except Exception as e:

Datei anzeigen

@@ -42,7 +42,7 @@ def batch_create():
valid_from = request.form['valid_from'] valid_from = request.form['valid_from']
valid_until = request.form['valid_until'] valid_until = request.form['valid_until']
device_limit = int(request.form['device_limit']) device_limit = int(request.form['device_limit'])
is_test = 'is_test' in request.form is_fake = 'is_fake' in request.form
# Validierung # Validierung
if count < 1 or count > 100: if count < 1 or count > 100:
@@ -74,13 +74,13 @@ def batch_create():
INSERT INTO licenses ( INSERT INTO licenses (
license_key, customer_id, license_key, customer_id,
license_type, valid_from, valid_until, device_limit, license_type, valid_from, valid_until, device_limit,
is_test, created_at is_fake, created_at
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
RETURNING id RETURNING id
""", ( """, (
license_key, customer_id, license_key, customer_id,
license_type, valid_from, valid_until, device_limit, license_type, valid_from, valid_until, device_limit,
is_test, datetime.now() is_fake, datetime.now()
)) ))
license_id = cur.fetchone()[0] license_id = cur.fetchone()[0]
@@ -141,7 +141,7 @@ def batch_export():
SELECT SELECT
l.license_key, c.name, c.email, l.license_key, c.name, c.email,
l.license_type, l.valid_from, l.valid_until, l.license_type, l.valid_from, l.valid_until,
l.device_limit, l.is_test, l.created_at l.device_limit, l.is_fake, l.created_at
FROM licenses l FROM licenses l
JOIN customers c ON l.customer_id = c.id JOIN customers c ON l.customer_id = c.id
WHERE l.id = ANY(%s) WHERE l.id = ANY(%s)
@@ -158,7 +158,7 @@ def batch_export():
'valid_from': row[4], 'valid_from': row[4],
'valid_until': row[5], 'valid_until': row[5],
'device_limit': row[6], 'device_limit': row[6],
'is_test': row[7], 'is_fake': row[7],
'created_at': row[8] 'created_at': row[8]
}) })
@@ -334,13 +334,13 @@ def batch_import():
INSERT INTO licenses ( INSERT INTO licenses (
license_key, customer_id, license_key, customer_id,
license_type, valid_from, valid_until, device_limit, license_type, valid_from, valid_until, device_limit,
is_test, created_at is_fake, created_at
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
RETURNING id RETURNING id
""", ( """, (
license_key, customer_id, license_key, customer_id,
row['license_type'], row['valid_from'], row['valid_until'], row['license_type'], row['valid_from'], row['valid_until'],
int(row['device_limit']), row.get('is_test', False), int(row['device_limit']), row.get('is_fake', False),
datetime.now() datetime.now()
)) ))

Datei anzeigen

@@ -22,14 +22,14 @@ def test_customers():
@customer_bp.route("/customers") @customer_bp.route("/customers")
@login_required @login_required
def customers(): def customers():
show_test = request.args.get('show_test', 'false').lower() == 'true' show_fake = request.args.get('show_fake', 'false').lower() == 'true'
search = request.args.get('search', '').strip() search = request.args.get('search', '').strip()
page = request.args.get('page', 1, type=int) page = request.args.get('page', 1, type=int)
per_page = 20 per_page = 20
sort = request.args.get('sort', 'name') sort = request.args.get('sort', 'name')
order = request.args.get('order', 'asc') order = request.args.get('order', 'asc')
customers_list = get_customers(show_test=show_test, search=search) customers_list = get_customers(show_fake=show_fake, search=search)
# Sortierung # Sortierung
if sort == 'name': if sort == 'name':
@@ -48,7 +48,7 @@ def customers():
return render_template("customers.html", return render_template("customers.html",
customers=paginated_customers, customers=paginated_customers,
show_test=show_test, show_fake=show_fake,
search=search, search=search,
page=page, page=page,
per_page=per_page, per_page=per_page,
@@ -77,17 +77,17 @@ def edit_customer(customer_id):
new_values = { new_values = {
'name': request.form['name'], 'name': request.form['name'],
'email': request.form['email'], 'email': request.form['email'],
'is_test': 'is_test' in request.form 'is_fake': 'is_fake' in request.form
} }
cur.execute(""" cur.execute("""
UPDATE customers UPDATE customers
SET name = %s, email = %s, is_test = %s SET name = %s, email = %s, is_fake = %s
WHERE id = %s WHERE id = %s
""", ( """, (
new_values['name'], new_values['name'],
new_values['email'], new_values['email'],
new_values['is_test'], new_values['is_fake'],
customer_id customer_id
)) ))
@@ -98,16 +98,16 @@ def edit_customer(customer_id):
old_values={ old_values={
'name': current_customer['name'], 'name': current_customer['name'],
'email': current_customer['email'], 'email': current_customer['email'],
'is_test': current_customer.get('is_test', False) 'is_fake': current_customer.get('is_fake', False)
}, },
new_values=new_values) new_values=new_values)
flash('Kunde erfolgreich aktualisiert!', 'success') flash('Kunde erfolgreich aktualisiert!', 'success')
# Redirect mit show_test Parameter wenn nötig # Redirect mit show_fake Parameter wenn nötig
redirect_url = url_for('customers.customers_licenses') redirect_url = url_for('customers.customers_licenses')
if request.form.get('show_test') == 'true': if request.form.get('show_fake') == 'true':
redirect_url += '?show_test=true' redirect_url += '?show_fake=true'
return redirect(redirect_url) return redirect(redirect_url)
finally: finally:
cur.close() cur.close()
@@ -137,13 +137,13 @@ def create_customer():
# Insert new customer # Insert new customer
name = request.form['name'] name = request.form['name']
email = request.form['email'] email = request.form['email']
is_test = 'is_test' in request.form # Checkbox ist nur vorhanden wenn angekreuzt is_fake = 'is_fake' in request.form # Checkbox ist nur vorhanden wenn angekreuzt
cur.execute(""" cur.execute("""
INSERT INTO customers (name, email, is_test, created_at) INSERT INTO customers (name, email, is_fake, created_at)
VALUES (%s, %s, %s, %s) VALUES (%s, %s, %s, %s)
RETURNING id RETURNING id
""", (name, email, is_test, datetime.now())) """, (name, email, is_fake, datetime.now()))
customer_id = cur.fetchone()[0] customer_id = cur.fetchone()[0]
conn.commit() conn.commit()
@@ -153,17 +153,17 @@ def create_customer():
new_values={ new_values={
'name': name, 'name': name,
'email': email, 'email': email,
'is_test': is_test 'is_fake': is_fake
}) })
if is_test: if is_fake:
flash(f'Testkunde {name} erfolgreich erstellt!', 'success') flash(f'Fake-Kunde {name} erfolgreich erstellt!', 'success')
else: else:
flash(f'Kunde {name} erfolgreich erstellt!', 'success') flash(f'Kunde {name} erfolgreich erstellt!', 'success')
# Redirect mit show_test=true wenn Testkunde erstellt wurde # Redirect mit show_fake=true wenn Fake-Kunde erstellt wurde
if is_test: if is_fake:
return redirect(url_for('customers.customers_licenses', show_test='true')) return redirect(url_for('customers.customers_licenses', show_fake='true'))
else: else:
return redirect(url_for('customers.customers_licenses')) return redirect(url_for('customers.customers_licenses'))
@@ -232,9 +232,9 @@ def customers_licenses():
import psycopg2 import psycopg2
logging.info("=== CUSTOMERS-LICENSES ROUTE CALLED ===") logging.info("=== CUSTOMERS-LICENSES ROUTE CALLED ===")
# Get show_test parameter from URL # Get show_fake parameter from URL
show_test = request.args.get('show_test', 'false').lower() == 'true' show_fake = request.args.get('show_fake', 'false').lower() == 'true'
logging.info(f"show_test parameter: {show_test}") logging.info(f"show_fake parameter: {show_fake}")
try: try:
# Direkte Verbindung ohne Helper-Funktionen # Direkte Verbindung ohne Helper-Funktionen
@@ -250,7 +250,7 @@ def customers_licenses():
try: try:
# Hole alle Kunden mit ihren Lizenzen # Hole alle Kunden mit ihren Lizenzen
# Wenn show_test=false, zeige nur Nicht-Test-Kunden # Wenn show_fake=false, zeige nur Nicht-Test-Kunden
query = """ query = """
SELECT SELECT
c.id, c.id,
@@ -259,17 +259,17 @@ def customers_licenses():
c.created_at, c.created_at,
COUNT(l.id), COUNT(l.id),
COUNT(CASE WHEN l.is_active = true THEN 1 END), COUNT(CASE WHEN l.is_active = true THEN 1 END),
COUNT(CASE WHEN l.is_test = true THEN 1 END), COUNT(CASE WHEN l.is_fake = true THEN 1 END),
MAX(l.created_at), MAX(l.created_at),
c.is_test c.is_fake
FROM customers c FROM customers c
LEFT JOIN licenses l ON c.id = l.customer_id LEFT JOIN licenses l ON c.id = l.customer_id
WHERE (%s OR c.is_test = false) WHERE (%s OR c.is_fake = false)
GROUP BY c.id, c.name, c.email, c.created_at, c.is_test GROUP BY c.id, c.name, c.email, c.created_at, c.is_fake
ORDER BY c.name ORDER BY c.name
""" """
cur.execute(query, (show_test,)) cur.execute(query, (show_fake,))
customers = [] customers = []
results = cur.fetchall() results = cur.fetchall()
@@ -286,12 +286,12 @@ def customers_licenses():
'active_licenses': row[5], 'active_licenses': row[5],
'test_licenses': row[6], 'test_licenses': row[6],
'last_license_created': row[7], 'last_license_created': row[7],
'is_test': row[8] 'is_fake': row[8]
}) })
return render_template("customers_licenses.html", return render_template("customers_licenses.html",
customers=customers, customers=customers,
show_test=show_test) show_fake=show_fake)
finally: finally:
cur.close() cur.close()
@@ -325,7 +325,7 @@ def api_customer_licenses(customer_id):
l.license_key, l.license_key,
l.license_type, l.license_type,
l.is_active, l.is_active,
l.is_test, l.is_fake,
l.valid_from, l.valid_from,
l.valid_until, l.valid_until,
l.device_limit, l.device_limit,
@@ -387,7 +387,7 @@ def api_customer_licenses(customer_id):
'license_key': row[1], 'license_key': row[1],
'license_type': row[2], 'license_type': row[2],
'is_active': row[3], 'is_active': row[3],
'is_test': row[4], 'is_fake': row[4],
'valid_from': row[5].strftime('%Y-%m-%d') if row[5] else None, 'valid_from': row[5].strftime('%Y-%m-%d') if row[5] else None,
'valid_until': row[6].strftime('%Y-%m-%d') if row[6] else None, 'valid_until': row[6].strftime('%Y-%m-%d') if row[6] else None,
'device_limit': row[7], 'device_limit': row[7],
@@ -442,7 +442,7 @@ def api_customer_quick_stats(customer_id):
SELECT SELECT
COUNT(l.id) as total_licenses, COUNT(l.id) as total_licenses,
COUNT(CASE WHEN l.is_active = true THEN 1 END) as active_licenses, COUNT(CASE WHEN l.is_active = true THEN 1 END) as active_licenses,
COUNT(CASE WHEN l.is_test = true THEN 1 END) as test_licenses, COUNT(CASE WHEN l.is_fake = true THEN 1 END) as test_licenses,
SUM(l.device_limit) as total_device_limit SUM(l.device_limit) as total_device_limit
FROM licenses l FROM licenses l
WHERE l.customer_id = %s WHERE l.customer_id = %s

Datei anzeigen

@@ -21,10 +21,10 @@ def export_licenses():
try: try:
# Filter aus Request # Filter aus Request
show_test = request.args.get('show_test', 'false') == 'true' show_fake = request.args.get('show_fake', 'false') == 'true'
# SQL Query mit optionalem Test-Filter # SQL Query mit optionalem Test-Filter
if show_test: if show_fake:
query = """ query = """
SELECT SELECT
l.id, l.id,
@@ -37,7 +37,7 @@ def export_licenses():
l.is_active, l.is_active,
l.device_limit, l.device_limit,
l.created_at, l.created_at,
l.is_test, l.is_fake,
CASE CASE
WHEN l.valid_until < CURRENT_DATE THEN 'Abgelaufen' WHEN l.valid_until < CURRENT_DATE THEN 'Abgelaufen'
WHEN l.is_active = false THEN 'Deaktiviert' WHEN l.is_active = false THEN 'Deaktiviert'
@@ -62,7 +62,7 @@ def export_licenses():
l.is_active, l.is_active,
l.device_limit, l.device_limit,
l.created_at, l.created_at,
l.is_test, l.is_fake,
CASE CASE
WHEN l.valid_until < CURRENT_DATE THEN 'Abgelaufen' WHEN l.valid_until < CURRENT_DATE THEN 'Abgelaufen'
WHEN l.is_active = false THEN 'Deaktiviert' WHEN l.is_active = false THEN 'Deaktiviert'
@@ -72,7 +72,7 @@ def export_licenses():
(SELECT COUNT(DISTINCT hardware_id) FROM sessions s WHERE s.license_key = l.license_key) as registered_devices (SELECT COUNT(DISTINCT hardware_id) FROM sessions s WHERE s.license_key = l.license_key) as registered_devices
FROM licenses l FROM licenses l
LEFT JOIN customers c ON l.customer_id = c.id LEFT JOIN customers c ON l.customer_id = c.id
WHERE l.is_test = false WHERE l.is_fake = false
ORDER BY l.created_at DESC ORDER BY l.created_at DESC
""" """
@@ -81,7 +81,7 @@ def export_licenses():
# Daten für Export vorbereiten # Daten für Export vorbereiten
data = [] data = []
columns = ['ID', 'Lizenzschlüssel', 'Kunde', 'E-Mail', 'Typ', 'Gültig von', columns = ['ID', 'Lizenzschlüssel', 'Kunde', 'E-Mail', 'Typ', 'Gültig von',
'Gültig bis', 'Aktiv', 'Gerätelimit', 'Erstellt am', 'Test-Lizenz', 'Gültig bis', 'Aktiv', 'Gerätelimit', 'Erstellt am', 'Fake-Lizenz',
'Status', 'Aktive Sessions', 'Registrierte Geräte'] 'Status', 'Aktive Sessions', 'Registrierte Geräte']
for row in cur.fetchall(): for row in cur.fetchall():
@@ -163,13 +163,13 @@ def export_customers():
c.phone, c.phone,
c.address, c.address,
c.created_at, c.created_at,
c.is_test, c.is_fake,
COUNT(l.id) as license_count, COUNT(l.id) as license_count,
COUNT(CASE WHEN l.is_active = true THEN 1 END) as active_licenses, COUNT(CASE WHEN l.is_active = true THEN 1 END) as active_licenses,
COUNT(CASE WHEN l.valid_until < CURRENT_DATE THEN 1 END) as expired_licenses COUNT(CASE WHEN l.valid_until < CURRENT_DATE THEN 1 END) as expired_licenses
FROM customers c FROM customers c
LEFT JOIN licenses l ON c.id = l.customer_id LEFT JOIN licenses l ON c.id = l.customer_id
GROUP BY c.id, c.name, c.email, c.phone, c.address, c.created_at, c.is_test GROUP BY c.id, c.name, c.email, c.phone, c.address, c.created_at, c.is_fake
ORDER BY c.name ORDER BY c.name
""") """)
@@ -227,7 +227,7 @@ def export_sessions():
s.last_heartbeat, s.last_heartbeat,
s.is_active, s.is_active,
l.license_type, l.license_type,
l.is_test l.is_fake
FROM sessions s FROM sessions s
LEFT JOIN licenses l ON s.license_key = l.license_key LEFT JOIN licenses l ON s.license_key = l.license_key
WHERE s.is_active = true WHERE s.is_active = true
@@ -247,7 +247,7 @@ def export_sessions():
s.last_heartbeat, s.last_heartbeat,
s.is_active, s.is_active,
l.license_type, l.license_type,
l.is_test l.is_fake
FROM sessions s FROM sessions s
LEFT JOIN licenses l ON s.license_key = l.license_key LEFT JOIN licenses l ON s.license_key = l.license_key
WHERE s.started_at >= CURRENT_TIMESTAMP - INTERVAL '%s days' WHERE s.started_at >= CURRENT_TIMESTAMP - INTERVAL '%s days'
@@ -259,7 +259,7 @@ def export_sessions():
data = [] data = []
columns = ['ID', 'Lizenzschlüssel', 'Kunde', 'Benutzer', 'Geräte-ID', columns = ['ID', 'Lizenzschlüssel', 'Kunde', 'Benutzer', 'Geräte-ID',
'Login-Zeit', 'Logout-Zeit', 'Letzte Aktivität', 'Aktiv', 'Login-Zeit', 'Logout-Zeit', 'Letzte Aktivität', 'Aktiv',
'Lizenztyp', 'Test-Lizenz'] 'Lizenztyp', 'Fake-Lizenz']
for row in cur.fetchall(): for row in cur.fetchall():
data.append(list(row)) data.append(list(row))
@@ -295,7 +295,7 @@ def export_resources():
# Filter aus Request # Filter aus Request
resource_type = request.args.get('type', 'all') resource_type = request.args.get('type', 'all')
status_filter = request.args.get('status', 'all') status_filter = request.args.get('status', 'all')
show_test = request.args.get('show_test', 'false') == 'true' show_fake = request.args.get('show_fake', 'false') == 'true'
# SQL Query aufbauen # SQL Query aufbauen
query = """ query = """
@@ -304,7 +304,7 @@ def export_resources():
rp.resource_type, rp.resource_type,
rp.resource_value, rp.resource_value,
rp.status, rp.status,
rp.is_test, rp.is_fake,
l.license_key, l.license_key,
c.name as customer_name, c.name as customer_name,
rp.created_at, rp.created_at,
@@ -328,8 +328,8 @@ def export_resources():
query += " AND rp.status = %s" query += " AND rp.status = %s"
params.append(status_filter) params.append(status_filter)
if not show_test: if not show_fake:
query += " AND rp.is_test = false" query += " AND rp.is_fake = false"
query += " ORDER BY rp.resource_type, rp.resource_value" query += " ORDER BY rp.resource_type, rp.resource_value"

Datei anzeigen

@@ -24,43 +24,47 @@ def licenses():
# Get filter parameters # Get filter parameters
search = request.args.get('search', '').strip() search = request.args.get('search', '').strip()
filter_type = request.args.get('type', '') filter_types = request.args.getlist('types[]') # Multi-select for types
filter_status = request.args.get('status', '') filter_statuses = request.args.getlist('statuses[]') # Multi-select for statuses
sort = request.args.get('sort', 'created_at') sort = request.args.get('sort', 'created_at')
order = request.args.get('order', 'desc') order = request.args.get('order', 'desc')
page = request.args.get('page', 1, type=int) page = request.args.get('page', 1, type=int)
per_page = 50 per_page = 50
# Process type filter to determine show_test # Get all licenses (both fake and real data)
# Default: show only real data unless test_data is explicitly selected licenses_list = get_licenses(show_fake=True)
show_test = filter_type == 'test_data'
# Get licenses based on filters # Type filtering with OR logic
licenses_list = get_licenses(show_test=show_test) if filter_types:
filtered_by_type = []
for license in licenses_list:
# Check if license matches any selected type
if 'full' in filter_types and license.get('license_type') == 'full' and not license.get('is_fake'):
filtered_by_type.append(license)
elif 'test' in filter_types and license.get('license_type') == 'test' and not license.get('is_fake'):
filtered_by_type.append(license)
licenses_list = filtered_by_type
else:
# If no types selected, show only real data by default
licenses_list = [l for l in licenses_list if not l.get('is_fake')]
# Additional filtering based on type and status # Status filtering with OR logic
if filter_type: if filter_statuses:
if filter_type == 'full':
licenses_list = [l for l in licenses_list if l.get('license_type') == 'full' and not l.get('is_test')]
elif filter_type == 'test':
licenses_list = [l for l in licenses_list if l.get('license_type') == 'test' and not l.get('is_test')]
elif filter_type == 'test_data':
licenses_list = [l for l in licenses_list if l.get('is_test')]
elif filter_type == 'live_data':
licenses_list = [l for l in licenses_list if not l.get('is_test')]
# Status filtering
if filter_status:
now = datetime.now() now = datetime.now()
if filter_status == 'active': filtered_by_status = []
licenses_list = [l for l in licenses_list if l.get('is_active') and l.get('valid_until') and l.get('valid_until') > now] for license in licenses_list:
elif filter_status == 'expiring': # Check if license matches any selected status
if 'active' in filter_statuses and license.get('is_active') and license.get('valid_until') and license.get('valid_until') > now:
filtered_by_status.append(license)
elif 'expiring' in filter_statuses:
expiry_threshold = now + timedelta(days=30) expiry_threshold = now + timedelta(days=30)
licenses_list = [l for l in licenses_list if l.get('valid_until') and now < l.get('valid_until') <= expiry_threshold] if license.get('valid_until') and now < license.get('valid_until') <= expiry_threshold:
elif filter_status == 'expired': filtered_by_status.append(license)
licenses_list = [l for l in licenses_list if l.get('valid_until') and l.get('valid_until') <= now] elif 'expired' in filter_statuses and license.get('valid_until') and license.get('valid_until') <= now:
elif filter_status == 'inactive': filtered_by_status.append(license)
licenses_list = [l for l in licenses_list if not l.get('is_active')] elif 'inactive' in filter_statuses and not license.get('is_active'):
filtered_by_status.append(license)
licenses_list = filtered_by_status
# Search filtering # Search filtering
if search: if search:
@@ -79,10 +83,9 @@ def licenses():
return render_template("licenses.html", return render_template("licenses.html",
licenses=licenses_list, licenses=licenses_list,
show_test=show_test,
search=search, search=search,
filter_type=filter_type, filter_types=filter_types,
filter_status=filter_status, filter_statuses=filter_statuses,
sort=sort, sort=sort,
order=order, order=order,
page=page, page=page,
@@ -114,14 +117,14 @@ def edit_license(license_id):
'valid_from': request.form['valid_from'], 'valid_from': request.form['valid_from'],
'valid_until': request.form['valid_until'], 'valid_until': request.form['valid_until'],
'is_active': 'is_active' in request.form, 'is_active': 'is_active' in request.form,
'is_test': 'is_test' in request.form, 'is_fake': 'is_fake' in request.form,
'device_limit': int(request.form.get('device_limit', 3)) 'device_limit': int(request.form.get('device_limit', 3))
} }
cur.execute(""" cur.execute("""
UPDATE licenses UPDATE licenses
SET license_key = %s, license_type = %s, valid_from = %s, SET license_key = %s, license_type = %s, valid_from = %s,
valid_until = %s, is_active = %s, is_test = %s, device_limit = %s valid_until = %s, is_active = %s, is_fake = %s, device_limit = %s
WHERE id = %s WHERE id = %s
""", ( """, (
new_values['license_key'], new_values['license_key'],
@@ -129,7 +132,7 @@ def edit_license(license_id):
new_values['valid_from'], new_values['valid_from'],
new_values['valid_until'], new_values['valid_until'],
new_values['is_active'], new_values['is_active'],
new_values['is_test'], new_values['is_fake'],
new_values['device_limit'], new_values['device_limit'],
license_id license_id
)) ))
@@ -144,7 +147,7 @@ def edit_license(license_id):
'valid_from': str(current_license.get('valid_from', '')), 'valid_from': str(current_license.get('valid_from', '')),
'valid_until': str(current_license.get('valid_until', '')), 'valid_until': str(current_license.get('valid_until', '')),
'is_active': current_license.get('is_active'), 'is_active': current_license.get('is_active'),
'is_test': current_license.get('is_test'), 'is_fake': current_license.get('is_fake'),
'device_limit': current_license.get('device_limit', 3) 'device_limit': current_license.get('device_limit', 3)
}, },
new_values=new_values) new_values=new_values)
@@ -283,7 +286,7 @@ def create_license():
license_key = request.form["license_key"].upper() # Immer Großbuchstaben license_key = request.form["license_key"].upper() # Immer Großbuchstaben
license_type = request.form["license_type"] license_type = request.form["license_type"]
valid_from = request.form["valid_from"] valid_from = request.form["valid_from"]
is_test = request.form.get("is_test") == "on" # Checkbox value is_fake = request.form.get("is_fake") == "on" # Checkbox value
# Berechne valid_until basierend auf Laufzeit # Berechne valid_until basierend auf Laufzeit
duration = int(request.form.get("duration", 1)) duration = int(request.form.get("duration", 1))
@@ -337,19 +340,19 @@ def create_license():
# Kunde einfügen (erbt Test-Status von Lizenz) # Kunde einfügen (erbt Test-Status von Lizenz)
cur.execute(""" cur.execute("""
INSERT INTO customers (name, email, is_test, created_at) INSERT INTO customers (name, email, is_fake, created_at)
VALUES (%s, %s, %s, NOW()) VALUES (%s, %s, %s, NOW())
RETURNING id RETURNING id
""", (name, email, is_test)) """, (name, email, is_fake))
customer_id = cur.fetchone()[0] customer_id = cur.fetchone()[0]
customer_info = {'name': name, 'email': email, 'is_test': is_test} customer_info = {'name': name, 'email': email, 'is_fake': is_fake}
# Audit-Log für neuen Kunden # Audit-Log für neuen Kunden
log_audit('CREATE', 'customer', customer_id, log_audit('CREATE', 'customer', customer_id,
new_values={'name': name, 'email': email, 'is_test': is_test}) new_values={'name': name, 'email': email, 'is_fake': is_fake})
else: else:
# Bestehender Kunde - hole Infos für Audit-Log # Bestehender Kunde - hole Infos für Audit-Log
cur.execute("SELECT name, email, is_test FROM customers WHERE id = %s", (customer_id,)) cur.execute("SELECT name, email, is_fake FROM customers WHERE id = %s", (customer_id,))
customer_data = cur.fetchone() customer_data = cur.fetchone()
if not customer_data: if not customer_data:
flash('Kunde nicht gefunden!', 'error') flash('Kunde nicht gefunden!', 'error')
@@ -357,17 +360,17 @@ def create_license():
customer_info = {'name': customer_data[0], 'email': customer_data[1]} customer_info = {'name': customer_data[0], 'email': customer_data[1]}
# Wenn Kunde Test-Kunde ist, Lizenz auch als Test markieren # Wenn Kunde Test-Kunde ist, Lizenz auch als Test markieren
if customer_data[2]: # is_test des Kunden if customer_data[2]: # is_fake des Kunden
is_test = True is_fake = True
# Lizenz hinzufügen # Lizenz hinzufügen
cur.execute(""" cur.execute("""
INSERT INTO licenses (license_key, customer_id, license_type, valid_from, valid_until, is_active, INSERT INTO licenses (license_key, customer_id, license_type, valid_from, valid_until, is_active,
domain_count, ipv4_count, phone_count, device_limit, is_test) domain_count, ipv4_count, phone_count, device_limit, is_fake)
VALUES (%s, %s, %s, %s, %s, TRUE, %s, %s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s, TRUE, %s, %s, %s, %s, %s)
RETURNING id RETURNING id
""", (license_key, customer_id, license_type, valid_from, valid_until, """, (license_key, customer_id, license_type, valid_from, valid_until,
domain_count, ipv4_count, phone_count, device_limit, is_test)) domain_count, ipv4_count, phone_count, device_limit, is_fake))
license_id = cur.fetchone()[0] license_id = cur.fetchone()[0]
# Ressourcen zuweisen # Ressourcen zuweisen
@@ -375,10 +378,10 @@ def create_license():
# Prüfe Verfügbarkeit # Prüfe Verfügbarkeit
cur.execute(""" cur.execute("""
SELECT SELECT
(SELECT COUNT(*) FROM resource_pools WHERE resource_type = 'domain' AND status = 'available' AND is_test = %s) as domains, (SELECT COUNT(*) FROM resource_pools WHERE resource_type = 'domain' AND status = 'available' AND is_fake = %s) as domains,
(SELECT COUNT(*) FROM resource_pools WHERE resource_type = 'ipv4' AND status = 'available' AND is_test = %s) as ipv4s, (SELECT COUNT(*) FROM resource_pools WHERE resource_type = 'ipv4' AND status = 'available' AND is_fake = %s) as ipv4s,
(SELECT COUNT(*) FROM resource_pools WHERE resource_type = 'phone' AND status = 'available' AND is_test = %s) as phones (SELECT COUNT(*) FROM resource_pools WHERE resource_type = 'phone' AND status = 'available' AND is_fake = %s) as phones
""", (is_test, is_test, is_test)) """, (is_fake, is_fake, is_fake))
available = cur.fetchone() available = cur.fetchone()
if available[0] < domain_count: if available[0] < domain_count:
@@ -392,9 +395,9 @@ def create_license():
if domain_count > 0: if domain_count > 0:
cur.execute(""" cur.execute("""
SELECT id FROM resource_pools SELECT id FROM resource_pools
WHERE resource_type = 'domain' AND status = 'available' AND is_test = %s WHERE resource_type = 'domain' AND status = 'available' AND is_fake = %s
LIMIT %s FOR UPDATE LIMIT %s FOR UPDATE
""", (is_test, domain_count)) """, (is_fake, domain_count))
for (resource_id,) in cur.fetchall(): for (resource_id,) in cur.fetchall():
cur.execute(""" cur.execute("""
UPDATE resource_pools UPDATE resource_pools
@@ -417,9 +420,9 @@ def create_license():
if ipv4_count > 0: if ipv4_count > 0:
cur.execute(""" cur.execute("""
SELECT id FROM resource_pools SELECT id FROM resource_pools
WHERE resource_type = 'ipv4' AND status = 'available' AND is_test = %s WHERE resource_type = 'ipv4' AND status = 'available' AND is_fake = %s
LIMIT %s FOR UPDATE LIMIT %s FOR UPDATE
""", (is_test, ipv4_count)) """, (is_fake, ipv4_count))
for (resource_id,) in cur.fetchall(): for (resource_id,) in cur.fetchall():
cur.execute(""" cur.execute("""
UPDATE resource_pools UPDATE resource_pools
@@ -442,9 +445,9 @@ def create_license():
if phone_count > 0: if phone_count > 0:
cur.execute(""" cur.execute("""
SELECT id FROM resource_pools SELECT id FROM resource_pools
WHERE resource_type = 'phone' AND status = 'available' AND is_test = %s WHERE resource_type = 'phone' AND status = 'available' AND is_fake = %s
LIMIT %s FOR UPDATE LIMIT %s FOR UPDATE
""", (is_test, phone_count)) """, (is_fake, phone_count))
for (resource_id,) in cur.fetchall(): for (resource_id,) in cur.fetchall():
cur.execute(""" cur.execute("""
UPDATE resource_pools UPDATE resource_pools
@@ -480,7 +483,7 @@ def create_license():
'valid_from': valid_from, 'valid_from': valid_from,
'valid_until': valid_until, 'valid_until': valid_until,
'device_limit': device_limit, 'device_limit': device_limit,
'is_test': is_test 'is_fake': is_fake
}) })
flash(f'Lizenz {license_key} erfolgreich erstellt!', 'success') flash(f'Lizenz {license_key} erfolgreich erstellt!', 'success')

Datei anzeigen

@@ -28,9 +28,9 @@ def resources():
resource_type = request.args.get('type', 'all') resource_type = request.args.get('type', 'all')
status_filter = request.args.get('status', 'all') status_filter = request.args.get('status', 'all')
search_query = request.args.get('search', '') search_query = request.args.get('search', '')
show_test = request.args.get('show_test', 'false') == 'true' show_fake = request.args.get('show_fake', 'false') == 'true'
logging.info(f"Filters: type={resource_type}, status={status_filter}, search={search_query}, show_test={show_test}") logging.info(f"Filters: type={resource_type}, status={status_filter}, search={search_query}, show_fake={show_fake}")
# Basis-Query # Basis-Query
query = """ query = """
@@ -39,7 +39,7 @@ def resources():
rp.resource_type, rp.resource_type,
rp.resource_value, rp.resource_value,
rp.status, rp.status,
rp.is_test, rp.is_fake,
rp.allocated_to_license, rp.allocated_to_license,
rp.created_at, rp.created_at,
rp.status_changed_at, rp.status_changed_at,
@@ -67,8 +67,8 @@ def resources():
query += " AND (rp.resource_value ILIKE %s OR c.name ILIKE %s)" query += " AND (rp.resource_value ILIKE %s OR c.name ILIKE %s)"
params.extend([f'%{search_query}%', f'%{search_query}%']) params.extend([f'%{search_query}%', f'%{search_query}%'])
if not show_test: if not show_fake:
query += " AND rp.is_test = false" query += " AND rp.is_fake = false"
query += " ORDER BY rp.resource_type, rp.resource_value" query += " ORDER BY rp.resource_type, rp.resource_value"
@@ -84,7 +84,7 @@ def resources():
'resource_type': row[1], 'resource_type': row[1],
'resource_value': row[2], 'resource_value': row[2],
'status': row[3], 'status': row[3],
'is_test': row[4], 'is_fake': row[4],
'allocated_to_license': row[5], 'allocated_to_license': row[5],
'created_at': row[6], 'created_at': row[6],
'status_changed_at': row[7], 'status_changed_at': row[7],
@@ -98,16 +98,16 @@ def resources():
SELECT SELECT
resource_type, resource_type,
status, status,
is_test, is_fake,
COUNT(*) as count COUNT(*) as count
FROM resource_pools FROM resource_pools
""" """
# Apply test filter to statistics as well # Apply test filter to statistics as well
if not show_test: if not show_fake:
stats_query += " WHERE is_test = false" stats_query += " WHERE is_fake = false"
stats_query += " GROUP BY resource_type, status, is_test" stats_query += " GROUP BY resource_type, status, is_fake"
cur.execute(stats_query) cur.execute(stats_query)
@@ -115,7 +115,7 @@ def resources():
for row in cur.fetchall(): for row in cur.fetchall():
res_type = row[0] res_type = row[0]
status = row[1] status = row[1]
is_test = row[2] is_fake = row[2]
count = row[3] count = row[3]
if res_type not in stats: if res_type not in stats:
@@ -131,7 +131,7 @@ def resources():
stats[res_type]['total'] += count stats[res_type]['total'] += count
stats[res_type][status] = stats[res_type].get(status, 0) + count stats[res_type][status] = stats[res_type].get(status, 0) + count
if is_test: if is_fake:
stats[res_type]['test'] += count stats[res_type]['test'] += count
else: else:
stats[res_type]['prod'] += count stats[res_type]['prod'] += count
@@ -160,7 +160,7 @@ def resources():
resource_type=resource_type, resource_type=resource_type,
status_filter=status_filter, status_filter=status_filter,
search=search_query, # Changed from search_query to search search=search_query, # Changed from search_query to search
show_test=show_test, show_fake=show_fake,
total=total, total=total,
page=page, page=page,
total_pages=total_pages, total_pages=total_pages,
@@ -325,7 +325,7 @@ def resource_history(resource_id):
try: try:
# Hole Ressourcen-Info # Hole Ressourcen-Info
cur.execute(""" cur.execute("""
SELECT resource_type, resource_value, status, is_test SELECT resource_type, resource_value, status, is_fake
FROM resource_pools WHERE id = %s FROM resource_pools WHERE id = %s
""", (resource_id,)) """, (resource_id,))
resource = cur.fetchone() resource = cur.fetchone()
@@ -369,7 +369,7 @@ def resource_history(resource_id):
'type': resource[0], 'type': resource[0],
'value': resource[1], 'value': resource[1],
'status': resource[2], 'status': resource[2],
'is_test': resource[3] 'is_fake': resource[3]
}, },
history=history) history=history)
@@ -395,10 +395,10 @@ def resource_metrics():
SELECT SELECT
resource_type, resource_type,
status, status,
is_test, is_fake,
COUNT(*) as count COUNT(*) as count
FROM resource_pools FROM resource_pools
GROUP BY resource_type, status, is_test GROUP BY resource_type, status, is_fake
ORDER BY resource_type, status ORDER BY resource_type, status
""") """)
@@ -539,8 +539,8 @@ def resources_report():
COUNT(CASE WHEN status = 'available' THEN 1 END) as available, COUNT(CASE WHEN status = 'available' THEN 1 END) as available,
COUNT(CASE WHEN status = 'allocated' THEN 1 END) as allocated, COUNT(CASE WHEN status = 'allocated' THEN 1 END) as allocated,
COUNT(CASE WHEN status = 'quarantined' THEN 1 END) as quarantined, COUNT(CASE WHEN status = 'quarantined' THEN 1 END) as quarantined,
COUNT(CASE WHEN is_test = true THEN 1 END) as test, COUNT(CASE WHEN is_fake = true THEN 1 END) as test,
COUNT(CASE WHEN is_test = false THEN 1 END) as production COUNT(CASE WHEN is_fake = false THEN 1 END) as production
FROM resource_pools FROM resource_pools
GROUP BY resource_type GROUP BY resource_type
ORDER BY resource_type ORDER BY resource_type
@@ -566,7 +566,7 @@ def resources_report():
rp.resource_type, rp.resource_type,
rp.resource_value, rp.resource_value,
rp.status, rp.status,
rp.is_test, rp.is_fake,
c.name as customer_name, c.name as customer_name,
l.license_key, l.license_key,
rp.status_changed_at, rp.status_changed_at,
@@ -628,7 +628,7 @@ def add_resources():
try: try:
resource_type = request.form.get('resource_type') resource_type = request.form.get('resource_type')
resources_text = request.form.get('resources_text', '') resources_text = request.form.get('resources_text', '')
is_test = request.form.get('is_test', 'false') == 'true' is_fake = request.form.get('is_fake', 'false') == 'true'
if not resource_type or not resources_text.strip(): if not resource_type or not resources_text.strip():
flash('Bitte Ressourcentyp und Ressourcen angeben!', 'error') flash('Bitte Ressourcentyp und Ressourcen angeben!', 'error')
@@ -686,9 +686,9 @@ def add_resources():
for resource in new_resources: for resource in new_resources:
cur.execute(""" cur.execute("""
INSERT INTO resource_pools INSERT INTO resource_pools
(resource_type, resource_value, status, is_test, created_by) (resource_type, resource_value, status, is_fake, created_by)
VALUES (%s, %s, 'available', %s, %s) VALUES (%s, %s, 'available', %s, %s)
""", (resource_type, resource, is_test, session['username'])) """, (resource_type, resource, is_fake, session['username']))
added_count += 1 added_count += 1
conn.commit() conn.commit()
@@ -706,7 +706,7 @@ def add_resources():
if invalid_resources: if invalid_resources:
flash(f'{len(invalid_resources)} ungültige Ressourcen wurden ignoriert.', 'error') flash(f'{len(invalid_resources)} ungültige Ressourcen wurden ignoriert.', 'error')
return redirect(url_for('resources.resources', show_test=request.form.get('show_test', 'false'))) return redirect(url_for('resources.resources', show_fake=request.form.get('show_fake', 'false')))
except Exception as e: except Exception as e:
conn.rollback() conn.rollback()
@@ -717,5 +717,5 @@ def add_resources():
conn.close() conn.close()
# GET request - show form # GET request - show form
show_test = request.args.get('show_test', 'false') == 'true' show_fake = request.args.get('show_fake', 'false') == 'true'
return render_template('add_resources.html', show_test=show_test) return render_template('add_resources.html', show_fake=show_fake)

Datei anzeigen

@@ -175,9 +175,9 @@
<!-- Test Data Checkbox --> <!-- Test Data Checkbox -->
<div class="form-check mt-3"> <div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="isTest" name="is_test"> <input class="form-check-input" type="checkbox" id="isFake" name="is_fake">
<label class="form-check-label" for="isTest"> <label class="form-check-label" for="isFake">
<i class="fas fa-flask"></i> Als Testdaten markieren <i class="fas fa-flask"></i> Als Fake-Daten markieren
<small class="text-muted">(wird von der Software ignoriert)</small> <small class="text-muted">(wird von der Software ignoriert)</small>
</label> </label>
</div> </div>

Datei anzeigen

@@ -30,9 +30,9 @@
</div> </div>
<div class="form-check mt-3"> <div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="isTest" name="is_test"> <input class="form-check-input" type="checkbox" id="isFake" name="is_fake">
<label class="form-check-label" for="isTest"> <label class="form-check-label" for="isFake">
<i class="fas fa-flask"></i> Als Testdaten markieren <i class="fas fa-flask"></i> Als Fake-Daten markieren
<small class="text-muted">(Kunde wird von der Software ignoriert)</small> <small class="text-muted">(Kunde wird von der Software ignoriert)</small>
</label> </label>
</div> </div>

Datei anzeigen

@@ -5,10 +5,10 @@
{% macro sortable_header(label, field, current_sort, current_order) %} {% macro sortable_header(label, field, current_sort, current_order) %}
<th> <th>
{% if current_sort == field %} {% if current_sort == field %}
<a href="{{ url_for('customers.customers', sort=field, order='desc' if current_order == 'asc' else 'asc', search=search, show_test=show_test, page=1) }}" <a href="{{ url_for('customers.customers', sort=field, order='desc' if current_order == 'asc' else 'asc', search=search, show_fake=show_fake, page=1) }}"
class="server-sortable"> class="server-sortable">
{% else %} {% else %}
<a href="{{ url_for('customers.customers', sort=field, order='asc', search=search, show_test=show_test, page=1) }}" <a href="{{ url_for('customers.customers', sort=field, order='asc', search=search, show_fake=show_fake, page=1) }}"
class="server-sortable"> class="server-sortable">
{% endif %} {% endif %}
{{ label }} {{ label }}
@@ -41,10 +41,10 @@
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<div class="form-check mt-4"> <div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="show_test" name="show_test" value="true" <input class="form-check-input" type="checkbox" id="show_fake" name="show_fake" value="true"
{% if show_test %}checked{% endif %} onchange="this.form.submit()"> {% if show_fake %}checked{% endif %} onchange="this.form.submit()">
<label class="form-check-label" for="show_test"> <label class="form-check-label" for="show_fake">
🧪 Testdaten anzeigen 🧪 Fake-Daten anzeigen
</label> </label>
</div> </div>
</div> </div>
@@ -126,31 +126,31 @@
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
<!-- Erste Seite --> <!-- Erste Seite -->
<li class="page-item {% if page == 1 %}disabled{% endif %}"> <li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('customers.customers', page=1, search=search, sort=sort, order=order, show_test=show_test) }}">Erste</a> <a class="page-link" href="{{ url_for('customers.customers', page=1, search=search, sort=sort, order=order, show_fake=show_fake) }}">Erste</a>
</li> </li>
<!-- Vorherige Seite --> <!-- Vorherige Seite -->
<li class="page-item {% if page == 1 %}disabled{% endif %}"> <li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('customers.customers', page=page-1, search=search, sort=sort, order=order, show_test=show_test) }}"></a> <a class="page-link" href="{{ url_for('customers.customers', page=page-1, search=search, sort=sort, order=order, show_fake=show_fake) }}"></a>
</li> </li>
<!-- Seitenzahlen --> <!-- Seitenzahlen -->
{% for p in range(1, total_pages + 1) %} {% for p in range(1, total_pages + 1) %}
{% if p >= page - 2 and p <= page + 2 %} {% if p >= page - 2 and p <= page + 2 %}
<li class="page-item {% if p == page %}active{% endif %}"> <li class="page-item {% if p == page %}active{% endif %}">
<a class="page-link" href="{{ url_for('customers.customers', page=p, search=search, sort=sort, order=order, show_test=show_test) }}">{{ p }}</a> <a class="page-link" href="{{ url_for('customers.customers', page=p, search=search, sort=sort, order=order, show_fake=show_fake) }}">{{ p }}</a>
</li> </li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<!-- Nächste Seite --> <!-- Nächste Seite -->
<li class="page-item {% if page == total_pages %}disabled{% endif %}"> <li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('customers.customers', page=page+1, search=search, sort=sort, order=order, show_test=show_test) }}"></a> <a class="page-link" href="{{ url_for('customers.customers', page=page+1, search=search, sort=sort, order=order, show_fake=show_fake) }}"></a>
</li> </li>
<!-- Letzte Seite --> <!-- Letzte Seite -->
<li class="page-item {% if page == total_pages %}disabled{% endif %}"> <li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('customers.customers', page=total_pages, search=search, sort=sort, order=order, show_test=show_test) }}">Letzte</a> <a class="page-link" href="{{ url_for('customers.customers', page=total_pages, search=search, sort=sort, order=order, show_fake=show_fake) }}">Letzte</a>
</li> </li>
</ul> </ul>
<p class="text-center text-muted"> <p class="text-center text-muted">

Datei anzeigen

@@ -17,14 +17,14 @@
<i class="bi bi-download"></i> Export <i class="bi bi-download"></i> Export
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ url_for('export.export_customers', format='excel', include_test=request.args.get('show_test')) }}"> <li><a class="dropdown-item" href="{{ url_for('export.export_customers', format='excel', include_test=request.args.get('show_fake')) }}">
<i class="bi bi-file-earmark-excel text-success"></i> Kunden (Excel)</a></li> <i class="bi bi-file-earmark-excel text-success"></i> Kunden (Excel)</a></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_customers', format='csv', include_test=request.args.get('show_test')) }}"> <li><a class="dropdown-item" href="{{ url_for('export.export_customers', format='csv', include_test=request.args.get('show_fake')) }}">
<i class="bi bi-file-earmark-text"></i> Kunden (CSV)</a></li> <i class="bi bi-file-earmark-text"></i> Kunden (CSV)</a></li>
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_licenses', format='excel', include_test=request.args.get('show_test')) }}"> <li><a class="dropdown-item" href="{{ url_for('export.export_licenses', format='excel', include_test=request.args.get('show_fake')) }}">
<i class="bi bi-file-earmark-excel text-success"></i> Lizenzen (Excel)</a></li> <i class="bi bi-file-earmark-excel text-success"></i> Lizenzen (Excel)</a></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_licenses', format='csv', include_test=request.args.get('show_test')) }}"> <li><a class="dropdown-item" href="{{ url_for('export.export_licenses', format='csv', include_test=request.args.get('show_fake')) }}">
<i class="bi bi-file-earmark-text"></i> Lizenzen (CSV)</a></li> <i class="bi bi-file-earmark-text"></i> Lizenzen (CSV)</a></li>
</ul> </ul>
</div> </div>
@@ -47,11 +47,11 @@
<input type="text" class="form-control mb-2" id="customerSearch" <input type="text" class="form-control mb-2" id="customerSearch"
placeholder="Kunde suchen..." autocomplete="off"> placeholder="Kunde suchen..." autocomplete="off">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="showTestCustomers" <input class="form-check-input" type="checkbox" id="showFakeCustomers"
{% if request.args.get('show_test', 'false').lower() == 'true' %}checked{% endif %} {% if request.args.get('show_fake', 'false').lower() == 'true' %}checked{% endif %}
onchange="toggleTestCustomers()"> onchange="toggleFakeCustomers()">
<label class="form-check-label" for="showTestCustomers"> <label class="form-check-label" for="showFakeCustomers">
<small class="text-muted">Testkunden anzeigen</small> <small class="text-muted">Fake-Kunden anzeigen</small>
</label> </label>
</div> </div>
</div> </div>
@@ -289,7 +289,7 @@ function loadCustomerLicenses(customerId) {
}); });
document.querySelector(`[data-customer-id="${customerId}"]`).classList.add('active'); document.querySelector(`[data-customer-id="${customerId}"]`).classList.add('active');
// URL aktualisieren ohne Reload (behalte show_test Parameter) // URL aktualisieren ohne Reload (behalte show_fake Parameter)
const currentUrl = new URL(window.location); const currentUrl = new URL(window.location);
currentUrl.searchParams.set('customer_id', customerId); currentUrl.searchParams.set('customer_id', customerId);
window.history.pushState({}, '', currentUrl.toString()); window.history.pushState({}, '', currentUrl.toString());
@@ -522,10 +522,10 @@ function copyToClipboard(text) {
} }
// Toggle Testkunden // Toggle Testkunden
function toggleTestCustomers() { function toggleFakeCustomers() {
const showTest = document.getElementById('showTestCustomers').checked; const showTest = document.getElementById('showFakeCustomers').checked;
const currentUrl = new URL(window.location); const currentUrl = new URL(window.location);
currentUrl.searchParams.set('show_test', showTest); currentUrl.searchParams.set('show_fake', showTest);
window.location.href = currentUrl.toString(); window.location.href = currentUrl.toString();
} }
@@ -854,12 +854,12 @@ function quarantineResource(resourceId, resourceValue) {
// Lade verfügbare Ressourcen // Lade verfügbare Ressourcen
function loadAvailableResources(licenseId) { function loadAvailableResources(licenseId) {
// Hole show_test Parameter aus der URL // Hole show_fake Parameter aus der URL
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const showTest = urlParams.get('show_test') === 'true'; const showTest = urlParams.get('show_fake') === 'true';
// Lade verfügbare Domains // Lade verfügbare Domains
fetch(`/api/resources/check-availability?type=domain&count=200&show_test=${showTest}`) fetch(`/api/resources/check-availability?type=domain&count=200&show_fake=${showTest}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
const select = document.getElementById('availableDomains'); const select = document.getElementById('availableDomains');
@@ -880,7 +880,7 @@ function loadAvailableResources(licenseId) {
}); });
// Lade verfügbare IPv4s // Lade verfügbare IPv4s
fetch(`/api/resources/check-availability?type=ipv4&count=200&show_test=${showTest}`) fetch(`/api/resources/check-availability?type=ipv4&count=200&show_fake=${showTest}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
const select = document.getElementById('availableIpv4s'); const select = document.getElementById('availableIpv4s');
@@ -901,7 +901,7 @@ function loadAvailableResources(licenseId) {
}); });
// Lade verfügbare Telefonnummern // Lade verfügbare Telefonnummern
fetch(`/api/resources/check-availability?type=phone&count=200&show_test=${showTest}`) fetch(`/api/resources/check-availability?type=phone&count=200&show_fake=${showTest}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
const select = document.getElementById('availablePhones'); const select = document.getElementById('availablePhones');

Datei anzeigen

@@ -116,17 +116,17 @@
<p class="text-muted">Vollversionen</p> <p class="text-muted">Vollversionen</p>
</div> </div>
<div class="col-6 text-center"> <div class="col-6 text-center">
<h3 class="text-warning">{{ stats.test_licenses }}</h3> <h3 class="text-warning">{{ stats.fake_licenses }}</h3>
<p class="text-muted">Testversionen</p> <p class="text-muted">Testversionen</p>
</div> </div>
</div> </div>
{% if stats.test_data_count > 0 or stats.test_customers_count > 0 or stats.test_resources_count > 0 %} {% if stats.fake_data_count > 0 or stats.fake_customers_count > 0 or stats.fake_resources_count > 0 %}
<div class="alert alert-info mt-3 mb-0"> <div class="alert alert-info mt-3 mb-0">
<small> <small>
<i class="fas fa-flask"></i> Testdaten: <i class="fas fa-flask"></i> Fake-Daten:
{{ stats.test_data_count }} Lizenzen, {{ stats.fake_data_count }} Lizenzen,
{{ stats.test_customers_count }} Kunden, {{ stats.fake_customers_count }} Kunden,
{{ stats.test_resources_count }} Ressourcen {{ stats.fake_resources_count }} Ressourcen
</small> </small>
</div> </div>
{% endif %} {% endif %}

Datei anzeigen

@@ -7,15 +7,15 @@
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<h2>Kunde bearbeiten</h2> <h2>Kunde bearbeiten</h2>
<div> <div>
<a href="{{ url_for('customers.customers_licenses', show_test=request.args.get('show_test')) }}" class="btn btn-secondary">👥 Zurück zur Übersicht</a> <a href="{{ url_for('customers.customers_licenses', show_fake=request.args.get('show_fake')) }}" class="btn btn-secondary">👥 Zurück zur Übersicht</a>
</div> </div>
</div> </div>
<div class="card mb-4"> <div class="card mb-4">
<div class="card-body"> <div class="card-body">
<form method="post" action="{{ url_for('customers.edit_customer', customer_id=customer.id) }}" accept-charset="UTF-8"> <form method="post" action="{{ url_for('customers.edit_customer', customer_id=customer.id) }}" accept-charset="UTF-8">
{% if request.args.get('show_test') == 'true' %} {% if request.args.get('show_fake') == 'true' %}
<input type="hidden" name="show_test" value="true"> <input type="hidden" name="show_fake" value="true">
{% endif %} {% endif %}
<div class="row g-3"> <div class="row g-3">
<div class="col-md-6"> <div class="col-md-6">
@@ -33,16 +33,16 @@
</div> </div>
<div class="form-check mt-3"> <div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="isTest" name="is_test" {% if customer.is_test %}checked{% endif %}> <input class="form-check-input" type="checkbox" id="isTest" name="is_fake" {% if customer.is_fake %}checked{% endif %}>
<label class="form-check-label" for="isTest"> <label class="form-check-label" for="isTest">
<i class="fas fa-flask"></i> Als Testdaten markieren <i class="fas fa-flask"></i> Als Fake-Daten markieren
<small class="text-muted">(Kunde und seine Lizenzen werden von der Software ignoriert)</small> <small class="text-muted">(Kunde und seine Lizenzen werden von der Software ignoriert)</small>
</label> </label>
</div> </div>
<div class="mt-4"> <div class="mt-4">
<button type="submit" class="btn btn-primary">💾 Änderungen speichern</button> <button type="submit" class="btn btn-primary">💾 Änderungen speichern</button>
<a href="{{ url_for('customers.customers_licenses', show_test=request.args.get('show_test')) }}" class="btn btn-secondary">Abbrechen</a> <a href="{{ url_for('customers.customers_licenses', show_fake=request.args.get('show_fake')) }}" class="btn btn-secondary">Abbrechen</a>
</div> </div>
</form> </form>
</div> </div>

Datei anzeigen

@@ -7,15 +7,15 @@
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<h2>Lizenz bearbeiten</h2> <h2>Lizenz bearbeiten</h2>
<div> <div>
<a href="{{ url_for('customers.customers_licenses', show_test=request.args.get('show_test')) }}" class="btn btn-secondary">📋 Zurück zur Übersicht</a> <a href="{{ url_for('customers.customers_licenses', show_fake=request.args.get('show_fake')) }}" class="btn btn-secondary">📋 Zurück zur Übersicht</a>
</div> </div>
</div> </div>
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<form method="post" action="{{ url_for('licenses.edit_license', license_id=license.id) }}" accept-charset="UTF-8"> <form method="post" action="{{ url_for('licenses.edit_license', license_id=license.id) }}" accept-charset="UTF-8">
{% if request.args.get('show_test') == 'true' %} {% if request.args.get('show_fake') == 'true' %}
<input type="hidden" name="show_test" value="true"> <input type="hidden" name="show_fake" value="true">
{% endif %} {% endif %}
<div class="row g-3"> <div class="row g-3">
<div class="col-md-6"> <div class="col-md-6">
@@ -66,16 +66,16 @@
</div> </div>
<div class="form-check mt-3"> <div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="isTest" name="is_test" {% if license.is_test %}checked{% endif %}> <input class="form-check-input" type="checkbox" id="isTest" name="is_fake" {% if license.is_fake %}checked{% endif %}>
<label class="form-check-label" for="isTest"> <label class="form-check-label" for="isTest">
<i class="fas fa-flask"></i> Als Testdaten markieren <i class="fas fa-flask"></i> Als Fake-Daten markieren
<small class="text-muted">(wird von der Software ignoriert)</small> <small class="text-muted">(wird von der Software ignoriert)</small>
</label> </label>
</div> </div>
<div class="mt-4"> <div class="mt-4">
<button type="submit" class="btn btn-primary">💾 Änderungen speichern</button> <button type="submit" class="btn btn-primary">💾 Änderungen speichern</button>
<a href="{{ url_for('customers.customers_licenses', show_test=request.args.get('show_test')) }}" class="btn btn-secondary">Abbrechen</a> <a href="{{ url_for('customers.customers_licenses', show_fake=request.args.get('show_fake')) }}" class="btn btn-secondary">Abbrechen</a>
</div> </div>
</form> </form>
</div> </div>

Datei anzeigen

@@ -154,9 +154,9 @@
<!-- Test Data Checkbox --> <!-- Test Data Checkbox -->
<div class="form-check mt-3"> <div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="isTest" name="is_test"> <input class="form-check-input" type="checkbox" id="isTest" name="is_fake">
<label class="form-check-label" for="isTest"> <label class="form-check-label" for="isTest">
<i class="fas fa-flask"></i> Als Testdaten markieren <i class="fas fa-flask"></i> Als Fake-Daten markieren
<small class="text-muted">(wird von der Software ignoriert)</small> <small class="text-muted">(wird von der Software ignoriert)</small>
</label> </label>
</div> </div>

Datei anzeigen

@@ -4,13 +4,20 @@
{% macro sortable_header(label, field, current_sort, current_order) %} {% macro sortable_header(label, field, current_sort, current_order) %}
<th> <th>
{% set base_url = url_for('licenses.licenses') %}
{% set params = [] %}
{% if search %}{% set _ = params.append('search=' + search|urlencode) %}{% endif %}
{% for type in filter_types %}{% set _ = params.append('types[]=' + type|urlencode) %}{% endfor %}
{% for status in filter_statuses %}{% set _ = params.append('statuses[]=' + status|urlencode) %}{% endfor %}
{% set _ = params.append('sort=' + field) %}
{% if current_sort == field %} {% if current_sort == field %}
<a href="{{ url_for('licenses.licenses', sort=field, order='desc' if current_order == 'asc' else 'asc', search=search, type=filter_type, status=filter_status, page=1) }}" {% set _ = params.append('order=' + ('desc' if current_order == 'asc' else 'asc')) %}
class="server-sortable">
{% else %} {% else %}
<a href="{{ url_for('licenses.licenses', sort=field, order='asc', search=search, type=filter_type, status=filter_status, page=1) }}" {% set _ = params.append('order=asc') %}
class="server-sortable">
{% endif %} {% endif %}
{% set _ = params.append('page=1') %}
<a href="{{ base_url }}?{{ params|join('&') }}" class="server-sortable">
{{ label }} {{ label }}
<span class="sort-indicator{% if current_sort == field %} active{% endif %}"> <span class="sort-indicator{% if current_sort == field %} active{% endif %}">
{% if current_sort == field %} {% if current_sort == field %}
@@ -24,6 +31,46 @@
{% endmacro %} {% endmacro %}
{% block extra_css %} {% block extra_css %}
<style>
.filter-group {
background-color: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin-bottom: 15px;
}
.filter-group h6 {
color: #495057;
font-weight: 600;
}
.badge.bg-info {
background-color: #0dcaf0 !important;
font-size: 0.75rem;
}
.active-filters .badge {
font-size: 0.875rem;
padding: 0.35em 0.65em;
}
.active-filters a {
text-decoration: none;
opacity: 0.8;
}
.active-filters a:hover {
opacity: 1;
}
#advancedFilters {
transition: all 0.3s ease;
}
.btn-sm {
padding: 0.375rem 0.75rem;
}
</style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@@ -36,46 +83,162 @@
<div class="card mb-3"> <div class="card mb-3">
<div class="card-body"> <div class="card-body">
<form method="get" action="{{ url_for('licenses.licenses') }}" id="filterForm"> <form method="get" action="{{ url_for('licenses.licenses') }}" id="filterForm">
<div class="row g-3 align-items-end"> <!-- Suchfeld -->
<div class="col-md-4"> <div class="row g-3 mb-3">
<div class="col-md-8">
<label for="search" class="form-label">🔍 Suchen</label> <label for="search" class="form-label">🔍 Suchen</label>
<input type="text" class="form-control" id="search" name="search" <input type="text" class="form-control" id="search" name="search"
placeholder="Lizenzschlüssel, Kunde, E-Mail..." placeholder="Lizenzschlüssel, Kunde, E-Mail..."
value="{{ search }}"> value="{{ search }}">
</div> </div>
<div class="col-md-2"> <div class="col-md-4 text-end">
<label for="type" class="form-label">Typ</label> <label class="form-label d-block">&nbsp;</label>
<select class="form-select" id="type" name="type"> <button type="button" class="btn btn-outline-primary me-2" onclick="toggleFilters()">
<option value="">Alle Typen</option> <i class="bi bi-funnel"></i> Filter
<option value="full" {% if filter_type == 'full' %}selected{% endif %}>Vollversion</option> </button>
<option value="test" {% if filter_type == 'test' %}selected{% endif %}>Testversion</option> <a href="{{ url_for('licenses.licenses') }}" class="btn btn-outline-secondary">
<option value="test_data" {% if filter_type == 'test_data' %}selected{% endif %}>🧪 Testdaten</option> <i class="bi bi-arrow-clockwise"></i> Zurücksetzen
<option value="live_data" {% if filter_type == 'live_data' %}selected{% endif %}>🚀 Live-Daten</option> </a>
</select>
</div>
<div class="col-md-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option value="">Alle Status</option>
<option value="active" {% if filter_status == 'active' %}selected{% endif %}>✅ Aktiv</option>
<option value="expiring" {% if filter_status == 'expiring' %}selected{% endif %}>⏰ Läuft bald ab</option>
<option value="expired" {% if filter_status == 'expired' %}selected{% endif %}>⚠️ Abgelaufen</option>
<option value="inactive" {% if filter_status == 'inactive' %}selected{% endif %}>❌ Deaktiviert</option>
</select>
</div>
<div class="col-md-3">
<a href="{{ url_for('licenses.licenses') }}" class="btn btn-outline-secondary">Zurücksetzen</a>
</div> </div>
</div> </div>
<!-- Erweiterter Filterbereich -->
<div id="advancedFilters" class="collapse {{ 'show' if filter_types or filter_status else '' }}">
<hr class="my-3">
<!-- Lizenztyp Filter (OR Logic) -->
<div class="filter-group mb-3">
<div class="d-flex align-items-center mb-2">
<h6 class="mb-0 me-2">Lizenztyp</h6>
<span class="badge bg-info">ODER</span>
</div>
<div class="row g-2">
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="types[]" value="full" id="typeFullversion"
{% if 'full' in (filter_types or []) %}checked{% endif %}>
<label class="form-check-label" for="typeFullversion">
<span class="badge bg-success">Vollversion</span>
</label>
</div>
</div>
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="types[]" value="test" id="typeTestversion"
{% if 'test' in (filter_types or []) %}checked{% endif %}>
<label class="form-check-label" for="typeTestversion">
<span class="badge bg-warning">Testversion</span>
</label>
</div>
</div>
</div>
</div>
<!-- Status Filter (OR Logic) -->
<div class="filter-group mb-3">
<div class="d-flex align-items-center mb-2">
<h6 class="mb-0 me-2">Status</h6>
<span class="badge bg-info">ODER</span>
</div>
<div class="row g-2">
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="statuses[]" value="active" id="statusActive"
{% if 'active' in (filter_statuses or []) %}checked{% endif %}>
<label class="form-check-label" for="statusActive">
✅ Aktiv
</label>
</div>
</div>
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="statuses[]" value="expiring" id="statusExpiring"
{% if 'expiring' in (filter_statuses or []) %}checked{% endif %}>
<label class="form-check-label" for="statusExpiring">
⏰ Läuft bald ab
</label>
</div>
</div>
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="statuses[]" value="expired" id="statusExpired"
{% if 'expired' in (filter_statuses or []) %}checked{% endif %}>
<label class="form-check-label" for="statusExpired">
⚠️ Abgelaufen
</label>
</div>
</div>
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="statuses[]" value="inactive" id="statusInactive"
{% if 'inactive' in (filter_statuses or []) %}checked{% endif %}>
<label class="form-check-label" for="statusInactive">
❌ Deaktiviert
</label>
</div>
</div>
</div>
</div>
<!-- Quick Filters / Presets -->
<div class="filter-group">
<h6 class="mb-2">Schnellfilter</h6>
<div class="row g-2">
<div class="col-auto">
<button type="button" class="btn btn-sm btn-outline-primary" onclick="applyPreset('expiring_soon')">
<i class="bi bi-clock-history"></i> Läuft in 7 Tagen ab
</button>
</div>
<div class="col-auto">
<button type="button" class="btn btn-sm btn-outline-primary" onclick="applyPreset('all_test')">
<i class="bi bi-bug"></i> Alle Testversionen
</button>
</div>
<div class="col-auto">
<button type="button" class="btn btn-sm btn-outline-primary" onclick="applyPreset('active_full')">
<i class="bi bi-shield-check"></i> Aktive Vollversionen
</button>
</div>
</div>
</div>
</div>
<!-- Hidden fields for sorting -->
<input type="hidden" name="sort" value="{{ sort }}">
<input type="hidden" name="order" value="{{ order }}">
</form> </form>
{% if search or filter_type or filter_status %} {% if search or filter_types or filter_statuses %}
<div class="mt-2"> <div class="mt-2">
<div class="d-flex align-items-center justify-content-between">
<small class="text-muted"> <small class="text-muted">
Gefiltert: {{ total }} Ergebnisse Gefiltert: {{ total }} Ergebnisse
{% if search %} | Suche: <strong>{{ search }}</strong>{% endif %}
{% if filter_type %} | Typ: <strong>{{ 'Vollversion' if filter_type == 'full' else 'Testversion' }}</strong>{% endif %}
{% if filter_status %} | Status: <strong>{{ filter_status }}</strong>{% endif %}
</small> </small>
<div class="active-filters">
{% if search %}
<span class="badge bg-secondary me-1">
<i class="bi bi-search"></i> {{ search }}
{% set clear_search_params = [] %}
{% for type in filter_types %}{% set _ = clear_search_params.append('types[]=' + type|urlencode) %}{% endfor %}
{% for status in filter_statuses %}{% set _ = clear_search_params.append('statuses[]=' + status|urlencode) %}{% endfor %}
{% set _ = clear_search_params.append('sort=' + sort) %}
{% set _ = clear_search_params.append('order=' + order) %}
<a href="{{ url_for('licenses.licenses') }}?{{ clear_search_params|join('&') }}" class="text-white ms-1">&times;</a>
</span>
{% endif %}
{% for type in (filter_types or []) %}
<span class="badge bg-primary me-1">
{{ 'Vollversion' if type == 'full' else 'Testversion' }}
<a href="#" onclick="removeFilter('type', '{{ type }}')" class="text-white ms-1">&times;</a>
</span>
{% endfor %}
{% for status in (filter_statuses or []) %}
<span class="badge bg-info me-1">
{% if status == 'active' %}✅ Aktiv{% elif status == 'expiring' %}⏰ Läuft bald ab{% elif status == 'expired' %}⚠️ Abgelaufen{% else %}❌ Deaktiviert{% endif %}
<a href="#" onclick="removeFilter('status', '{{ status }}')" class="text-white ms-1">&times;</a>
</span>
{% endfor %}
</div>
</div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
@@ -119,8 +282,8 @@
</td> </td>
<td> <td>
{{ license.customer_name }} {{ license.customer_name }}
{% if license.is_test %} {% if license.is_fake %}
<span class="badge bg-secondary ms-1" title="Testdaten">🧪</span> <span class="badge bg-secondary ms-1" title="Fake-Daten">🧪</span>
{% endif %} {% endif %}
</td> </td>
<td>-</td> <td>-</td>
@@ -182,33 +345,41 @@
{% if total_pages > 1 %} {% if total_pages > 1 %}
<nav aria-label="Seitennavigation" class="mt-3"> <nav aria-label="Seitennavigation" class="mt-3">
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
{% set base_url = url_for('licenses.licenses') %}
{% set base_params = [] %}
{% if search %}{% set _ = base_params.append('search=' + search|urlencode) %}{% endif %}
{% for type in filter_types %}{% set _ = base_params.append('types[]=' + type|urlencode) %}{% endfor %}
{% for status in filter_statuses %}{% set _ = base_params.append('statuses[]=' + status|urlencode) %}{% endfor %}
{% set _ = base_params.append('sort=' + sort) %}
{% set _ = base_params.append('order=' + order) %}
<!-- Erste Seite --> <!-- Erste Seite -->
<li class="page-item {% if page == 1 %}disabled{% endif %}"> <li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('licenses.licenses', page=1, search=search, type=filter_type, status=filter_status, sort=sort, order=order) }}">Erste</a> <a class="page-link" href="{{ base_url }}?{{ base_params|join('&') }}&page=1">Erste</a>
</li> </li>
<!-- Vorherige Seite --> <!-- Vorherige Seite -->
<li class="page-item {% if page == 1 %}disabled{% endif %}"> <li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('licenses.licenses', page=page-1, search=search, type=filter_type, status=filter_status, sort=sort, order=order) }}"></a> <a class="page-link" href="{{ base_url }}?{{ base_params|join('&') }}&page={{ page-1 }}"></a>
</li> </li>
<!-- Seitenzahlen --> <!-- Seitenzahlen -->
{% for p in range(1, total_pages + 1) %} {% for p in range(1, total_pages + 1) %}
{% if p >= page - 2 and p <= page + 2 %} {% if p >= page - 2 and p <= page + 2 %}
<li class="page-item {% if p == page %}active{% endif %}"> <li class="page-item {% if p == page %}active{% endif %}">
<a class="page-link" href="{{ url_for('licenses.licenses', page=p, search=search, type=filter_type, status=filter_status, sort=sort, order=order) }}">{{ p }}</a> <a class="page-link" href="{{ base_url }}?{{ base_params|join('&') }}&page={{ p }}">{{ p }}</a>
</li> </li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<!-- Nächste Seite --> <!-- Nächste Seite -->
<li class="page-item {% if page == total_pages %}disabled{% endif %}"> <li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('licenses.licenses', page=page+1, search=search, type=filter_type, status=filter_status, sort=sort, order=order) }}"></a> <a class="page-link" href="{{ base_url }}?{{ base_params|join('&') }}&page={{ page+1 }}"></a>
</li> </li>
<!-- Letzte Seite --> <!-- Letzte Seite -->
<li class="page-item {% if page == total_pages %}disabled{% endif %}"> <li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('licenses.licenses', page=total_pages, search=search, type=filter_type, status=filter_status, sort=sort, order=order) }}">Letzte</a> <a class="page-link" href="{{ base_url }}?{{ base_params|join('&') }}&page={{ total_pages }}">Letzte</a>
</li> </li>
</ul> </ul>
<p class="text-center text-muted"> <p class="text-center text-muted">
@@ -235,12 +406,58 @@
{% block extra_js %} {% block extra_js %}
<script> <script>
// Toggle Filter Panel
function toggleFilters() {
const filtersDiv = document.getElementById('advancedFilters');
const bsCollapse = new bootstrap.Collapse(filtersDiv, {
toggle: true
});
}
// Apply Preset Filters
function applyPreset(preset) {
const form = document.getElementById('filterForm');
// Clear existing filters
document.querySelectorAll('input[name="types[]"]').forEach(cb => cb.checked = false);
document.querySelectorAll('input[name="statuses[]"]').forEach(cb => cb.checked = false);
switch(preset) {
case 'expiring_soon':
document.getElementById('statusExpiring').checked = true;
break;
case 'all_test':
document.getElementById('typeTestversion').checked = true;
break;
case 'active_full':
document.getElementById('typeFullversion').checked = true;
document.getElementById('statusActive').checked = true;
break;
}
form.submit();
}
// Remove Individual Filter
function removeFilter(filterType, value) {
const form = document.getElementById('filterForm');
if (filterType === 'type') {
const checkbox = document.querySelector(`input[name="types[]"][value="${value}"]`);
if (checkbox) checkbox.checked = false;
} else if (filterType === 'status') {
const checkbox = document.querySelector(`input[name="statuses[]"][value="${value}"]`);
if (checkbox) checkbox.checked = false;
}
form.submit();
return false;
}
// Live Filtering // Live Filtering
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const filterForm = document.getElementById('filterForm'); const filterForm = document.getElementById('filterForm');
const searchInput = document.getElementById('search'); const searchInput = document.getElementById('search');
const typeSelect = document.getElementById('type');
const statusSelect = document.getElementById('status');
// Debounce timer für Suchfeld // Debounce timer für Suchfeld
let searchTimeout; let searchTimeout;
@@ -253,13 +470,11 @@ document.addEventListener('DOMContentLoaded', function() {
}, 300); }, 300);
}); });
// Live-Filter für Dropdowns (sofort) // Live-Filter für Checkboxen
typeSelect.addEventListener('change', function() { document.querySelectorAll('input[name="types[]"], input[name="statuses[]"]').forEach(checkbox => {
checkbox.addEventListener('change', function() {
filterForm.submit(); filterForm.submit();
}); });
statusSelect.addEventListener('change', function() {
filterForm.submit();
}); });
}); });

Datei anzeigen

@@ -225,7 +225,7 @@
<div class="card-body py-2"> <div class="card-body py-2">
<div class="form-check mb-0"> <div class="form-check mb-0">
<input class="form-check-input" type="checkbox" id="showTestResources" <input class="form-check-input" type="checkbox" id="showTestResources"
{% if show_test %}checked{% endif %} {% if show_fake %}checked{% endif %}
onchange="toggleTestResources()"> onchange="toggleTestResources()">
<label class="form-check-label" for="showTestResources"> <label class="form-check-label" for="showTestResources">
Testressourcen anzeigen Testressourcen anzeigen
@@ -293,7 +293,7 @@
<div class="card filter-card mb-4"> <div class="card filter-card mb-4">
<div class="card-body"> <div class="card-body">
<form method="get" action="{{ url_for('resources.resources') }}" id="filterForm"> <form method="get" action="{{ url_for('resources.resources') }}" id="filterForm">
<input type="hidden" name="show_test" value="{{ 'true' if show_test else 'false' }}"> <input type="hidden" name="show_fake" value="{{ 'true' if show_fake else 'false' }}">
<div class="row g-3"> <div class="row g-3">
<div class="col-md-3"> <div class="col-md-3">
<label for="type" class="form-label">🏷️ Typ</label> <label for="type" class="form-label">🏷️ Typ</label>
@@ -320,7 +320,7 @@
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label">&nbsp;</label> <label class="form-label">&nbsp;</label>
<a href="{{ url_for('resources.resources', show_test=show_test) }}" class="btn btn-secondary w-100"> <a href="{{ url_for('resources.resources', show_fake=show_fake) }}" class="btn btn-secondary w-100">
🔄 Zurücksetzen 🔄 Zurücksetzen
</a> </a>
</div> </div>
@@ -344,7 +344,7 @@
<i class="bi bi-download"></i> Export <i class="bi bi-download"></i> Export
</button> </button>
<ul class="dropdown-menu" aria-labelledby="exportDropdown"> <ul class="dropdown-menu" aria-labelledby="exportDropdown">
<li><a class="dropdown-item" href="{{ url_for('resources.resources_report', format='excel', type=resource_type, status=status_filter, search=search, show_test=show_test) }}"> <li><a class="dropdown-item" href="{{ url_for('resources.resources_report', format='excel', type=resource_type, status=status_filter, search=search, show_fake=show_fake) }}">
<i class="bi bi-file-earmark-excel text-success"></i> Excel Export</a></li> <i class="bi bi-file-earmark-excel text-success"></i> Excel Export</a></li>
</ul> </ul>
</div> </div>
@@ -359,7 +359,7 @@
<thead> <thead>
<tr> <tr>
<th width="80"> <th width="80">
<a href="{{ url_for('resources.resources', sort='id', order='desc' if sort_by == 'id' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}" <a href="{{ url_for('resources.resources', sort='id', order='desc' if sort_by == 'id' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_fake=show_fake) }}"
class="text-decoration-none text-dark sort-link"> class="text-decoration-none text-dark sort-link">
ID ID
{% if sort_by == 'id' %} {% if sort_by == 'id' %}
@@ -370,7 +370,7 @@
</a> </a>
</th> </th>
<th width="120"> <th width="120">
<a href="{{ url_for('resources.resources', sort='type', order='desc' if sort_by == 'type' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}" <a href="{{ url_for('resources.resources', sort='type', order='desc' if sort_by == 'type' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_fake=show_fake) }}"
class="text-decoration-none text-dark sort-link"> class="text-decoration-none text-dark sort-link">
Typ Typ
{% if sort_by == 'type' %} {% if sort_by == 'type' %}
@@ -381,7 +381,7 @@
</a> </a>
</th> </th>
<th> <th>
<a href="{{ url_for('resources.resources', sort='resource', order='desc' if sort_by == 'resource' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}" <a href="{{ url_for('resources.resources', sort='resource', order='desc' if sort_by == 'resource' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_fake=show_fake) }}"
class="text-decoration-none text-dark sort-link"> class="text-decoration-none text-dark sort-link">
Ressource Ressource
{% if sort_by == 'resource' %} {% if sort_by == 'resource' %}
@@ -392,7 +392,7 @@
</a> </a>
</th> </th>
<th width="140"> <th width="140">
<a href="{{ url_for('resources.resources', sort='status', order='desc' if sort_by == 'status' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}" <a href="{{ url_for('resources.resources', sort='status', order='desc' if sort_by == 'status' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_fake=show_fake) }}"
class="text-decoration-none text-dark sort-link"> class="text-decoration-none text-dark sort-link">
Status Status
{% if sort_by == 'status' %} {% if sort_by == 'status' %}
@@ -403,7 +403,7 @@
</a> </a>
</th> </th>
<th> <th>
<a href="{{ url_for('resources.resources', sort='assigned', order='desc' if sort_by == 'assigned' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}" <a href="{{ url_for('resources.resources', sort='assigned', order='desc' if sort_by == 'assigned' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_fake=show_fake) }}"
class="text-decoration-none text-dark sort-link"> class="text-decoration-none text-dark sort-link">
Zugewiesen an Zugewiesen an
{% if sort_by == 'assigned' %} {% if sort_by == 'assigned' %}
@@ -414,7 +414,7 @@
</a> </a>
</th> </th>
<th width="180"> <th width="180">
<a href="{{ url_for('resources.resources', sort='changed', order='desc' if sort_by == 'changed' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_test=show_test) }}" <a href="{{ url_for('resources.resources', sort='changed', order='desc' if sort_by == 'changed' and sort_order == 'asc' else 'asc', type=resource_type, status=status_filter, search=search, show_fake=show_fake) }}"
class="text-decoration-none text-dark sort-link"> class="text-decoration-none text-dark sort-link">
Letzte Änderung Letzte Änderung
{% if sort_by == 'changed' %} {% if sort_by == 'changed' %}
@@ -474,13 +474,13 @@
<td> <td>
{% if resource.customer_name %} {% if resource.customer_name %}
<div> <div>
<a href="{{ url_for('customers.customers_licenses', show_test=show_test) }}" <a href="{{ url_for('customers.customers_licenses', show_fake=show_fake) }}"
class="text-decoration-none"> class="text-decoration-none">
<strong>{{ resource.customer_name }}</strong> <strong>{{ resource.customer_name }}</strong>
</a> </a>
</div> </div>
<div class="small text-muted"> <div class="small text-muted">
<a href="{{ url_for('licenses.edit_license', license_id=resource.allocated_to_license) }}?ref=resources{{ '&show_test=true' if show_test else '' }}" <a href="{{ url_for('licenses.edit_license', license_id=resource.allocated_to_license) }}?ref=resources{{ '&show_fake=true' if show_fake else '' }}"
class="text-decoration-none text-muted"> class="text-decoration-none text-muted">
{{ resource.allocated_to_license }} {{ resource.allocated_to_license }}
</a> </a>
@@ -502,10 +502,10 @@
<td class="text-center"> <td class="text-center">
{% if resource.status == 'quarantine' %} {% if resource.status == 'quarantine' %}
<!-- Quick Action für Quarantäne --> <!-- Quick Action für Quarantäne -->
<form method="post" action="{{ url_for('resources.release', show_test=show_test, type=resource_type, status=status_filter, search=search) }}" <form method="post" action="{{ url_for('resources.release', show_fake=show_fake, type=resource_type, status=status_filter, search=search) }}"
style="display: inline-block; margin-right: 5px;"> style="display: inline-block; margin-right: 5px;">
<input type="hidden" name="resource_ids" value="{{ resource.id }}"> <input type="hidden" name="resource_ids" value="{{ resource.id }}">
<input type="hidden" name="show_test" value="{{ show_test }}"> <input type="hidden" name="show_fake" value="{{ show_fake }}">
<button type="submit" <button type="submit"
class="btn btn-sm btn-success"> class="btn btn-sm btn-success">
<i class="bi bi-check-circle"></i> Freigeben <i class="bi bi-check-circle"></i> Freigeben
@@ -546,7 +546,7 @@
{% if resource.allocated_to_license %} {% if resource.allocated_to_license %}
<li> <li>
<a class="dropdown-item" <a class="dropdown-item"
href="{{ url_for('licenses.edit_license', license_id=resource.allocated_to_license) }}?ref=resources{{ '&show_test=true' if show_test else '' }}"> href="{{ url_for('licenses.edit_license', license_id=resource.allocated_to_license) }}?ref=resources{{ '&show_fake=true' if show_fake else '' }}">
<i class="bi bi-file-text text-primary"></i> Lizenz bearbeiten <i class="bi bi-file-text text-primary"></i> Lizenz bearbeiten
</a> </a>
</li> </li>
@@ -554,7 +554,7 @@
{% if resource.id %} {% if resource.id %}
<li> <li>
<a class="dropdown-item" <a class="dropdown-item"
href="{{ url_for('customers.customers_licenses', customer_id=resource.id, show_test=show_test) }}"> href="{{ url_for('customers.customers_licenses', customer_id=resource.id, show_fake=show_fake) }}">
<i class="bi bi-person text-primary"></i> Kunde anzeigen <i class="bi bi-person text-primary"></i> Kunde anzeigen
</a> </a>
</li> </li>
@@ -562,10 +562,10 @@
{% elif resource.status == 'quarantine' %} {% elif resource.status == 'quarantine' %}
<!-- Aktionen für Quarantäne-Ressourcen --> <!-- Aktionen für Quarantäne-Ressourcen -->
<li> <li>
<form method="post" action="{{ url_for('resources.release', show_test=show_test, type=resource_type, status=status_filter, search=search) }}" <form method="post" action="{{ url_for('resources.release', show_fake=show_fake, type=resource_type, status=status_filter, search=search) }}"
style="display: contents;"> style="display: contents;">
<input type="hidden" name="resource_ids" value="{{ resource.id }}"> <input type="hidden" name="resource_ids" value="{{ resource.id }}">
<input type="hidden" name="show_test" value="{{ show_test }}"> <input type="hidden" name="show_fake" value="{{ show_fake }}">
<button type="submit" class="dropdown-item"> <button type="submit" class="dropdown-item">
<i class="bi bi-check-circle text-success"></i> Ressource freigeben <i class="bi bi-check-circle text-success"></i> Ressource freigeben
</button> </button>
@@ -614,13 +614,13 @@
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
<li class="page-item {% if page == 1 %}disabled{% endif %}"> <li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" <a class="page-link"
href="{{ url_for('resources.resources', page=1, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}"> href="{{ url_for('resources.resources', page=1, type=resource_type, status=status_filter, search=search, show_fake=show_fake, sort=sort_by, order=sort_order) }}">
<i class="bi bi-chevron-double-left"></i> Erste <i class="bi bi-chevron-double-left"></i> Erste
</a> </a>
</li> </li>
<li class="page-item {% if page == 1 %}disabled{% endif %}"> <li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" <a class="page-link"
href="{{ url_for('resources.resources', page=page-1, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}"> href="{{ url_for('resources.resources', page=page-1, type=resource_type, status=status_filter, search=search, show_fake=show_fake, sort=sort_by, order=sort_order) }}">
<i class="bi bi-chevron-left"></i> Zurück <i class="bi bi-chevron-left"></i> Zurück
</a> </a>
</li> </li>
@@ -629,7 +629,7 @@
{% if p == page or (p >= page - 2 and p <= page + 2) %} {% if p == page or (p >= page - 2 and p <= page + 2) %}
<li class="page-item {% if p == page %}active{% endif %}"> <li class="page-item {% if p == page %}active{% endif %}">
<a class="page-link" <a class="page-link"
href="{{ url_for('resources.resources', page=p, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}"> href="{{ url_for('resources.resources', page=p, type=resource_type, status=status_filter, search=search, show_fake=show_fake, sort=sort_by, order=sort_order) }}">
{{ p }} {{ p }}
</a> </a>
</li> </li>
@@ -638,13 +638,13 @@
<li class="page-item {% if page == total_pages %}disabled{% endif %}"> <li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" <a class="page-link"
href="{{ url_for('resources.resources', page=page+1, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}"> href="{{ url_for('resources.resources', page=page+1, type=resource_type, status=status_filter, search=search, show_fake=show_fake, sort=sort_by, order=sort_order) }}">
Weiter <i class="bi bi-chevron-right"></i> Weiter <i class="bi bi-chevron-right"></i>
</a> </a>
</li> </li>
<li class="page-item {% if page == total_pages %}disabled{% endif %}"> <li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" <a class="page-link"
href="{{ url_for('resources.resources', page=total_pages, type=resource_type, status=status_filter, search=search, show_test=show_test, sort=sort_by, order=sort_order) }}"> href="{{ url_for('resources.resources', page=total_pages, type=resource_type, status=status_filter, search=search, show_fake=show_fake, sort=sort_by, order=sort_order) }}">
Letzte <i class="bi bi-chevron-double-right"></i> Letzte <i class="bi bi-chevron-double-right"></i>
</a> </a>
</li> </li>
@@ -702,7 +702,7 @@
<div class="modal-content"> <div class="modal-content">
<form method="post" id="quarantineForm"> <form method="post" id="quarantineForm">
<!-- Filter-Parameter als Hidden Fields --> <!-- Filter-Parameter als Hidden Fields -->
<input type="hidden" name="show_test" value="{{ show_test }}"> <input type="hidden" name="show_fake" value="{{ show_fake }}">
<input type="hidden" name="type" value="{{ resource_type }}"> <input type="hidden" name="type" value="{{ resource_type }}">
<input type="hidden" name="status" value="{{ status_filter }}"> <input type="hidden" name="status" value="{{ status_filter }}">
<input type="hidden" name="search" value="{{ search }}"> <input type="hidden" name="search" value="{{ search }}">
@@ -852,7 +852,7 @@ function showQuarantineModal(resourceId) {
function toggleTestResources() { function toggleTestResources() {
const showTest = document.getElementById('showTestResources').checked; const showTest = document.getElementById('showTestResources').checked;
const currentUrl = new URL(window.location); const currentUrl = new URL(window.location);
currentUrl.searchParams.set('show_test', showTest); currentUrl.searchParams.set('show_fake', showTest);
window.location.href = currentUrl.toString(); window.location.href = currentUrl.toString();
} }

Datei anzeigen

@@ -63,7 +63,7 @@ def prepare_license_export_data(licenses):
format_datetime_for_export(license[8]), # Created At format_datetime_for_export(license[8]), # Created At
license[9], # Device Limit license[9], # Device Limit
license[10] or 0, # Current Devices license[10] or 0, # Current Devices
'Test' if license[11] else 'Full' # Is Test License 'Fake' if license[11] else 'Full' # Is Test License
]) ])
return export_data return export_data
@@ -141,7 +141,7 @@ def create_batch_export(licenses):
'Gültig von': format_datetime_for_export(license.get('valid_from')), 'Gültig von': format_datetime_for_export(license.get('valid_from')),
'Gültig bis': format_datetime_for_export(license.get('valid_until')), 'Gültig bis': format_datetime_for_export(license.get('valid_until')),
'Status': 'Aktiv' if license.get('is_active', True) else 'Inaktiv', 'Status': 'Aktiv' if license.get('is_active', True) else 'Inaktiv',
'Test-Lizenz': 'Ja' if license.get('is_test', False) else 'Nein' 'Fake-Lizenz': 'Ja' if license.get('is_test', False) else 'Nein'
}) })
df = pd.DataFrame(export_data) df = pd.DataFrame(export_data)