Files
stiftung-management-system/app/templates/stiftung/csv_import_mapping.html
SysAdmin Agent e0b377014c
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy (push) Has been cancelled
Code Quality / quality (push) Has been cancelled
v4.1.0: DMS email documents, category-specific Nachweis linking, version system
- Save cover email body as DMS document with new 'email' context type
- Show email body separately from attachments in email detail view
- Add per-category DMS document assignment in quarterly confirmation
  (Studiennachweis, Einkommenssituation, Vermögenssituation)
- Add VERSION file and context processor for automatic version display
- Add MCP server, agent system, import/export, and new migrations
- Update compose files and production environment template

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 18:48:52 +00:00

209 lines
9.3 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends 'base.html' %}
{% block title %}CSV Import Feldzuordnung{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1><i class="fas fa-columns text-primary"></i> Feldzuordnung: {{ import_label }}</h1>
<a href="{% url 'stiftung:import_export_hub' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> Zurück
</a>
</div>
<div class="alert alert-info">
<i class="fas fa-file-csv"></i>
<strong>{{ filename }}</strong> {{ total_rows }} Datenzeilen erkannt.
Ordnen Sie die CSV-Spalten den Datenbankfeldern zu. Nicht zugeordnete Spalten werden übersprungen.
</div>
<form method="post" action="{% url 'stiftung:csv_import_execute' %}">
{% csrf_token %}
<!-- Mapping Table -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">
<i class="fas fa-project-diagram"></i> Spalten zuordnen
</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th style="width: 5%;">#</th>
<th style="width: 25%;">CSV-Spalte</th>
<th style="width: 5%;"></th>
<th style="width: 30%;">Zuordnung</th>
<th style="width: 35%;">Vorschau</th>
</tr>
</thead>
<tbody>
{% for col in header_previews %}
<tr>
<td class="text-muted">{{ forloop.counter }}</td>
<td><code>{{ col.header }}</code></td>
<td class="text-center text-muted"><i class="fas fa-arrow-right"></i></td>
<td>
<select name="mapping_{{ forloop.counter0 }}" class="form-select form-select-sm mapping-select"
data-col-index="{{ forloop.counter0 }}">
<option value="__skip__"> Überspringen </option>
{% for field in model_fields %}
<option value="{{ field.1 }}">
{{ field.0 }}{% if field.3 %} *{% endif %}
({{ field.2 }})
</option>
{% endfor %}
</select>
</td>
<td>
<small class="text-muted">{{ col.preview|truncatechars:60 }}</small>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="card-footer">
<small class="text-muted">* = Pflichtfeld. Zuordnungen werden automatisch vorgeschlagen und können manuell geändert werden.</small>
</div>
</div>
<!-- Preview Table -->
{% if preview_rows %}
<div class="card shadow-sm mb-4">
<div class="card-header bg-light">
<h5 class="card-title mb-0">
<i class="fas fa-table"></i> Vorschau (erste {{ preview_rows|length }} Zeilen)
</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm table-striped mb-0">
<thead class="table-light">
<tr>
{% for col in header_previews %}
<th><small>{{ col.header }}</small></th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in preview_rows %}
<tr>
{% for cell in row %}
<td><small>{{ cell|truncatechars:40 }}</small></td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
<!-- Import Mode -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-light">
<h5 class="card-title mb-0">
<i class="fas fa-cog"></i> Import-Modus
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="radio" name="import_mode" id="mode_skip" value="skip" checked>
<label class="form-check-label" for="mode_skip">
<strong>Nur neue importieren</strong>
<br><small class="text-muted">Bereits vorhandene Einträge werden übersprungen</small>
</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="radio" name="import_mode" id="mode_merge" value="merge">
<label class="form-check-label" for="mode_merge">
<strong>Zusammenführen</strong>
<br><small class="text-muted">Vorhandene Einträge werden mit neuen Daten aktualisiert</small>
</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="radio" name="import_mode" id="mode_create" value="create">
<label class="form-check-label" for="mode_create">
<strong>Alle neu anlegen</strong>
<br><small class="text-muted">Keine Duplikatprüfung, alle Zeilen als neue Einträge</small>
</label>
</div>
</div>
</div>
</div>
</div>
<!-- Actions -->
<div class="d-flex justify-content-between">
<a href="{% url 'stiftung:import_export_hub' %}" class="btn btn-outline-secondary">
<i class="fas fa-times"></i> Abbrechen
</a>
<button type="submit" class="btn btn-primary btn-lg">
<i class="fas fa-upload"></i> {{ total_rows }} Zeilen importieren
</button>
</div>
</form>
</div>
</div>
{% endblock %}
{% block javascript %}
<script>
// Auto-apply mapping from server
const autoMapping = {{ auto_mapping_json|safe }};
document.addEventListener('DOMContentLoaded', function() {
for (const [colIdx, fieldName] of Object.entries(autoMapping)) {
const select = document.querySelector(`select[name="mapping_${colIdx}"]`);
if (select) {
for (const opt of select.options) {
if (opt.value === fieldName) {
opt.selected = true;
break;
}
}
}
}
// Highlight duplicate mappings
const selects = document.querySelectorAll('.mapping-select');
selects.forEach(sel => {
sel.addEventListener('change', highlightDuplicates);
});
highlightDuplicates();
});
function highlightDuplicates() {
const selects = document.querySelectorAll('.mapping-select');
const valueCount = {};
selects.forEach(sel => {
const val = sel.value;
if (val && val !== '__skip__') {
valueCount[val] = (valueCount[val] || 0) + 1;
}
});
selects.forEach(sel => {
const val = sel.value;
if (val && val !== '__skip__' && valueCount[val] > 1) {
sel.classList.add('is-invalid');
} else {
sel.classList.remove('is-invalid');
}
});
}
</script>
{% endblock %}