Compare commits

..

2 Commits

Author SHA1 Message Date
Aiirondev_dev 6a3865ef24 fix of the template name
Release Inventarsystem / release-docker (push) Successful in 2m16s
2026-08-12 11:46:59 +02:00
Aiirondev_dev 96d45710ac fix of the template name
Release Inventarsystem / release-docker (push) Successful in 2m15s
2026-08-12 11:45:48 +02:00
3 changed files with 82 additions and 60 deletions
+53 -37
View File
@@ -6366,11 +6366,25 @@ def edit_item(id):
return redirect(url_for('home_admin'))
def is_library_item(item):
"""
Prüft, ob ein Artikel ein Bibliotheks-Item ist.
- 'other', None oder Leerstring -> Inventarsystem (False)
- Jeder andere Medientyp ('Buch', 'CD', etc.) -> Bibliothek (True)
"""
if not item:
return False
item_type = item.get('ItemType', 'other')
if not item_type:
return False
return item_type.strip().lower() != 'other'
@app.route('/item_edit/<id>', methods=['GET', 'POST'])
def item_edit(id):
"""
Endpoint zum Bearbeiten von Artikeln (unterstützt Bibliotheks- und Inventar-Modus).
Complete endpoint for editing items. Automatically detects whether the item
is a Library item (ItemType != 'other') or an Inventory item (ItemType == 'other').
"""
if 'username' not in session:
if request.method == 'POST' and request.is_json:
@@ -6391,22 +6405,27 @@ def item_edit(id):
flash('Ungültige Element-ID.', 'error')
return redirect(url_for('home_admin'))
show_library = cfg.MODULES.is_enabled('library')
current_item = it.get_item(obj_id)
if not current_item:
flash('Element in der Datenbank nicht gefunden.', 'error')
return redirect(url_for('home_admin'))
# --- GET: Bearbeitungsformular laden ---
# Determine item type classification
library_module_active = cfg.MODULES.is_enabled('library')
is_lib_item = it.is_library_item(current_item)
show_library_features = library_module_active and is_lib_item
# -------------------------------------------------------------------
# GET METHOD: Render Form
# -------------------------------------------------------------------
if request.method == 'GET':
item = it.get_item(id)
if not item:
flash('Element nicht gefunden.', 'error')
return redirect(url_for('home_admin'))
current_item['_id'] = str(current_item['_id'])
item['_id'] = str(item['_id'])
# Gruppen-Codes aufteilen (Basis-Code vs. Einzelcodes für die Textarea)
base_code = item.get('Code_4', '')
# Format individual group codes for the textarea
base_code = current_item.get('Code_4', '')
individual_codes = []
if item.get('SeriesGroupId'):
group_ids = it.get_group_item_ids(str(item['_id']))
if current_item.get('SeriesGroupId'):
group_ids = it.get_group_item_ids(str(current_item['_id']))
if group_ids:
for gid in group_ids:
g_item = it.get_item(gid)
@@ -6414,26 +6433,23 @@ def item_edit(id):
if c4 and c4 != base_code:
individual_codes.append(c4)
item['IndividualCodes'] = '\n'.join(individual_codes)
current_item['IndividualCodes'] = '\n'.join(individual_codes)
return render_template(
'edit_library.html',
username=session['username'],
item=item,
show_library_features=show_library,
library_module_enabled=show_library,
page_title=f"Bearbeiten: {item.get('Name', '')}"
item=current_item,
show_library_features=show_library_features,
library_module_enabled=library_module_active,
page_title=f"Bearbeiten: {current_item.get('Name', '')}"
)
# --- POST: Änderungen speichern ---
# -------------------------------------------------------------------
# POST METHOD: Save Changes
# -------------------------------------------------------------------
redirect_target = request.referrer or url_for('home_admin')
current_item = it.get_item(obj_id)
if not current_item:
flash('Element in der Datenbank nicht gefunden.', 'error')
return redirect(redirect_target)
# Gemeinsame Felder auslesen & bereinigen
# Common fields
name = sanitize_form_value(request.form.get('name'))
ort = sanitize_form_value(request.form.get('ort'))
beschreibung = sanitize_form_value(request.form.get('beschreibung'))
@@ -6441,7 +6457,7 @@ def item_edit(id):
anschaffungs_kosten = sanitize_form_value(request.form.get('anschaffungskosten'))
reservierbar = 'reservierbar' in request.form
# Barcode & Gruppen-Codes
# Barcodes
code_4 = sanitize_form_value(request.form.get('code_4'))
individual_codes_raw = request.form.get('individual_codes', '')
@@ -6453,7 +6469,7 @@ def item_edit(id):
all_codes_to_check = [code_4] + individual_codes
# Uniqueness-Check für alle übergebenen Barcodes
# Barcode uniqueness check
current_group_id = current_item.get('SeriesGroupId')
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db_instance = client[cfg.MONGODB_DB]
@@ -6476,9 +6492,9 @@ def item_edit(id):
if has_code_error:
return redirect(redirect_target)
# --- Modi-spezifische Auswertung ---
if show_library:
# 1. Bibliotheks-Modus
# Type-specific processing
if show_library_features:
# --- LIBRARY ITEM ---
isbn_raw = sanitize_form_value(request.form.get('isbn', ''))
item_isbn = normalize_and_validate_isbn(isbn_raw) if isbn_raw else ''
item_type = sanitize_form_value(request.form.get('item_type_input', 'Buch'))
@@ -6489,16 +6505,16 @@ def item_edit(id):
filter3 = current_item.get('Filter3', [])
images = current_item.get('Images', [])
else:
# 2. Inventar-Modus
# --- INVENTORY ITEM ---
item_isbn = current_item.get('ISBN', '')
item_type = current_item.get('ItemType', 'other')
item_type = 'other' # Standard type for inventory items
library_category = current_item.get('library_category', '')
filter1 = expand_filter_selection(sanitize_form_value(request.form.getlist('filter')), 1)
filter2 = expand_filter_selection(sanitize_form_value(request.form.getlist('filter2')), 2)
filter3 = sanitize_form_value(request.form.getlist('filter3'))
# Bilderverarbeitung für den Inventar-Modus
# Manage images for Inventory Mode
images_to_keep = request.form.getlist('existing_images')
original_images = current_item.get('Images', [])
images = [img for img in original_images if img in images_to_keep]
@@ -6537,16 +6553,16 @@ def item_edit(id):
)
images.append(new_filename)
except Exception as e:
app.logger.error(f"Bild-Fehler bei Item {id}: {e}")
app.logger.error(f"Image error for item {id}: {e}")
# Standort bei Bedarf zu vordefinierten Orten hinzufügen
# Auto-add new location to predefined locations list if applicable
if ort and ort not in it.get_predefined_locations():
it.add_predefined_location(ort)
# 1. Gruppen-Codes synchronisieren
# Sync series/group barcode IDs
it.sync_group_codes(str(id), code_4, individual_codes)
# 2. Datenbank-Eintrag aktualisieren
# Update item in database
success = it.update_item(
id=str(id),
name=name,
+13
View File
@@ -27,6 +27,19 @@ from Web.modules.database.settings import MongoClient
import Web.modules.inventarsystem.data_protection as dp
def is_library_item(item):
"""
Determines if an item belongs to the library system.
Returns False for 'other', None, or empty ItemType (Inventory item).
Returns True for any specific library type ('Buch', 'CD', 'DVD', etc.).
"""
if not item:
return False
item_type = item.get('ItemType', 'other')
if not item_type:
return False
return str(item_type).strip().lower() != 'other'
def safe_decrypt_user(encrypted_user):
"""
Safely decrypt an encrypted username string.
+16 -23
View File
@@ -103,7 +103,7 @@
background-color: #0056b3;
}
/* Scanner Video Styles */
/* Scanner Video Canvas */
#code4-scanner video, #code4-scanner canvas,
#isbn-scanner video, #isbn-scanner canvas {
width: 100%;
@@ -124,7 +124,7 @@
overflow: hidden;
}
/* Image management grid */
/* Image Management */
.existing-images-grid {
display: flex;
flex-wrap: wrap;
@@ -197,7 +197,7 @@
<input type="hidden" name="item_id" value="{{ item._id }}">
{% if show_library_features %}
<!-- BIBLIOTHEKS-MODUS: ISBN / Barcode -->
<!-- ================= LIBRARY MODE FIELDS ================= -->
<div class="form-group">
<label for="isbn">ISBN / Barcode:</label>
<div class="isbn-input-group">
@@ -211,13 +211,12 @@
</div>
{% endif %}
<!-- Allgemein: Name -->
<!-- ================= COMMON FIELDS ================= -->
<div class="form-group">
<label for="name">Name / Titel:</label>
<input type="text" id="name" name="name" value="{{ item.Name|default('') }}" required>
</div>
<!-- Allgemein: Ort -->
<div class="form-group">
<label for="ort">Ort / Standort:</label>
<select id="ort" name="ort" data-selected="{{ item.Ort|default('') }}" required>
@@ -234,13 +233,11 @@
</div>
</div>
<!-- Allgemein: Beschreibung -->
<div class="form-group">
<label for="beschreibung">Beschreibung:</label>
<textarea id="beschreibung" name="beschreibung" required>{{ item.Beschreibung|default('') }}</textarea>
</div>
<!-- Barcode / Basis-Code -->
<div class="form-group" id="primary_code_group">
<label for="code_4">Basis-Code (Haupt-Barcode)</label>
<div style="display: flex; gap: 10px;">
@@ -251,7 +248,6 @@
<small id="code4-scan-status" class="form-text text-muted"></small>
</div>
<!-- Weitere Einzelcodes der Gruppe -->
<div class="form-group" id="individual_codes_group">
<label for="individual_codes">Weitere Einzelcodes der Gruppe (je Zeile ein Code)</label>
<textarea id="individual_codes" name="individual_codes" rows="4" class="form-control" placeholder="z.B.&#10;ABC-001&#10;ABC-002">{{ item.IndividualCodes|default('') }}</textarea>
@@ -259,7 +255,7 @@
</div>
{% if show_library_features %}
<!-- BIBLIOTHEKS-MODUS: Spezifische Filter -->
<!-- ================= LIBRARY CATEGORIZATION ================= -->
<div class="filter-inputs">
<h3>Medientyp</h3>
<div class="form-group">
@@ -268,7 +264,7 @@
<option value="Schulbuch" {% if item.ItemType == 'Schulbuch' %}selected{% endif %}>Schulbuch</option>
<option value="CD" {% if item.ItemType == 'CD' %}selected{% endif %}>CD</option>
<option value="DVD" {% if item.ItemType == 'DVD' %}selected{% endif %}>DVD</option>
<option value="Sonstiges" {% if item.ItemType == 'Sonstiges' or not item.ItemType %}selected{% endif %}>Sonstiges</option>
<option value="Sonstiges" {% if item.ItemType == 'Sonstiges' %}selected{% endif %}>Sonstiges</option>
</select>
</div>
<h3>Kategorie / Fach:</h3>
@@ -277,7 +273,7 @@
</div>
</div>
{% else %}
<!-- INVENTAR-MODUS: Standard 3-Stufen-Filter -->
<!-- ================= INVENTORY FILTERS (1-3) ================= -->
<div class="filter-inputs">
<h3>Unterrichtsfach (Filter 1):</h3>
<div class="multi-filter">
@@ -315,7 +311,7 @@
</div>
{% endif %}
<!-- Finanzen & Datum -->
<!-- ================= DATES & FINANCIALS ================= -->
<div class="form-group">
<label for="anschaffungsjahr">Anschaffungsjahr:</label>
<input type="date" id="anschaffungsjahr" name="anschaffungsjahr" value="{{ item.Anschaffungsjahr|default('') }}">
@@ -326,7 +322,7 @@
</div>
{% if not show_library_features %}
<!-- INVENTAR-MODUS: Bilder & Medien verwalten -->
<!-- ================= INVENTORY IMAGE MANAGEMENT ================= -->
<div class="form-group">
<label>Bestehende Bilder behalten:</label>
{% if item.Images and item.Images|length > 0 %}
@@ -350,7 +346,6 @@
</div>
{% endif %}
<!-- Reservierbar -->
<div class="form-group">
<label for="reservierbar" style="display:inline-block; width:auto; margin-right:10px;">Reservierbar:</label>
<input type="checkbox" id="reservierbar" name="reservierbar" style="width:auto;" {% if item.Reservierbar %}checked{% endif %}>
@@ -369,7 +364,7 @@
let code4LastScanned = '';
let code4LastScannedAt = 0;
// Filter-Optionen vom Server laden & bestehende Werte vorauswählen
// Load filter dropdown options and pre-select current item values
function loadAndSelectFilterValues(filterNumber) {
fetch(`/get_predefined_filter_values/${filterNumber}`)
.then(res => res.json())
@@ -380,7 +375,6 @@
const selectedValue = select.getAttribute('data-selected') || '';
// Optionen befüllen
data.values.forEach(val => {
if (!val || String(val).trim() === '') return;
const opt = document.createElement('option');
@@ -389,7 +383,7 @@
select.appendChild(opt);
});
// Falls der gewählte Wert nicht in den Vorgaben war, als Option ergänzen
// Add custom option if the item has a value not in predefined list
if (selectedValue && !Array.from(select.options).some(o => o.value === selectedValue)) {
const customOpt = document.createElement('option');
customOpt.value = selectedValue;
@@ -400,10 +394,10 @@
select.value = selectedValue;
}
})
.catch(err => console.error(`Fehler beim Laden von Filter ${filterNumber}:`, err));
.catch(err => console.error(`Error loading Filter ${filterNumber}:`, err));
}
// Orte vom Server laden
// Load locations
function loadLocationOptions() {
fetch('/get_predefined_locations')
.then(res => res.json())
@@ -423,10 +417,10 @@
select.value = currentVal;
})
.catch(err => console.error('Fehler beim Laden der Orte:', err));
.catch(err => console.error('Error loading locations:', err));
}
// Ort hinzufügen UI
// New location handler
function addNewLocation() {
const input = document.getElementById('new-location-input');
const val = input.value.trim();
@@ -446,7 +440,7 @@
document.getElementById('new-location-input').value = '';
}
// Quagga Barcode Scanner Initialisierung
// Quagga Barcode Scanner Engine
function runEngineInitialization(targetSelector, activeCallback, completionMsg, errorStatusSetter) {
if (scannerRunning) { Quagga.stop(); scannerRunning = false; }
activeScannerCallback = activeCallback;
@@ -470,7 +464,6 @@
if (typeof activeScannerCallback === "function") activeScannerCallback(String(data.codeResult.code).trim());
});
// Barcode Scanner für Basis-Code
function startCode4Scanner() {
const scannerBox = document.getElementById('code4-scanner');
const scanBtn = document.getElementById('scan-code4-btn');