Der grösste Funktionsblock von CluHu (~60 Versionen, v0.14.40–v0.16.52; neuer Wizard seit v0.28.128, Abschnitt «Etappe 3» unten). Wandelt beliebige Vereins-Excels in Mitgliedschaften, Personen und Verknüpfungen um.
⚠️ Die Abschnitte «Grundsätze» bis «Bekannte Grenzen» beschreiben den alten Zwei-Schritt-Wizard (bis v0.28.127). Seit v0.28.128 läuft der Import über den neuen Wizard (Formwahl → Spielfiguren → Upload → Prüfbildschirm → Spalten zuordnen → Vorgabe → Bilanz → stiller Import); die alte Analyse (Header-Erkennung, Detectors, Cluster-Engine, Splitter) arbeitet darunter weiter. Die alten Views bleiben bis Etappe 4 im Code, werden aber nicht mehr geroutet.
.xlsx (openpyxl, inkl. Merged-Cells und Header-Zellfarben), .xls (calamine), .csv (chardet-Encoding + Sniffer-Delimiter)apps/importer/detectors/) erkennen Spaltentypen mit Confidence: E-Mail, Telefon, PLZ-CH, Datum, IBAN, Mitgliedernummer, Vor-/Nachname, Strasse, Ort, Land, Status, Austritt, Jahrgang, Boolean, Dropdown, Mehrpersonen-Zelle, kombinierte Adresseswiss_names.py, ~4000 Vor-/Nachnamen): einzige Möglichkeit, „Müller Hans" (CH-Reihenfolge) von „Hans Müller" zu unterscheidententative-Flag. Formel garantiert: Header-Match schlägt immer Werte-only-Match__ignore__ möglich; User-Änderung = Confidence 1.0)multi_name_cell („Hans & Maria Müller" — Reihenfolge per Spalten-Mehrheits-Heuristik) und combined_address („Dorfstrasse 73, 8699 Bremgarten" — 4 Modi)select_for_update-Status-Claiming (preview → importing)sync_import_mappings legt die Verdrahtungsregeln an (PersonFieldMapping)Jeder Import schreibt /app/import_logs/[yymmdd]_[version]_[slug]/ mit Original-Datei, importierten CSVs, spaltenzuordnung.md (Auto-Erkennung vs. User-Override — Lern-Feedback für die Detectoren) und summary.md. Läuft auch bei Fehlschlägen und wirft nie Exceptions. Zweck laut Code: „damit wir aus dem Verhalten der Test-User lernen können."
Was der User bekommt: Unter der Dropzone steht die Karte «Noch keine Liste zur Hand?» mit Download der Datei CluHu_Beispiel_Mitgliederliste.xlsx: 30 fiktive Einzelpersonen, 12 Spalten (Mitgliedernummer, Name, Adresse, Handy, E-Mail, Geburtsdatum, Mitgliedschaft, Eintritt, Team), formatierte Kopfzeile, zweites Blatt «Erklärung» (bis v0.28.125 «Hinweise»). Seit v0.28.126 gibt es dieselbe Datei je Mitgliedschaftsform (?form=V Teilnehmende mit Erziehungsberechtigten, ?form=H Familien & Haushalte) aus einem gemeinsamen Datenstamm; die Upload-Karte zeigt bis zum neuen Wizard (Etappe 3) weiterhin die E-Datei. Sie dient zum Ausprobieren und als Vorlage. Dazu zwei Plausibilitätsprüfungen: Eine CSV aus einer einzigen Zeile (Zeilenumbrüche beim Copy & Paste verloren) oder eine Datei nur mit Kopfzeile bricht mit Klartext auf der Upload-Seite ab; der Batch wird verworfen.
Warum generiert statt statisch: Die Datei entsteht bei jedem Abruf aus apps/importer/sample_file.py. Keine Binärdatei im Repo, kein Drift zum Importer, Datumszellen und aktives Blatt bleiben kontrolliert. 30/30-Regel: Wer die Daten ändert, muss sicherstellen, dass der Import weiterhin 30 Mitglieder, 30 Personen, 0 Fehler, 0 Konflikte ergibt (eindeutige E-Mails auf example.com, Nummern, Nachnamen, Adressen, Handynummern; nur Einzelhaushalte).
Warum Hinweis statt Sackgasse: Vorher nahm der Wizard eine einzeilige CSV still als Kopfzeile mit 25 Pseudo-Spalten und 0 Datenzeilen. Das verletzt den Grundsatz «Nie stillschweigend».
Grössere Übernahmen aus Altsystemen (Firmen-Dubletten, Buchhaltung, Konten-Umschlüsselung) laufen per Script, nicht über den Wizard: siehe Migration Musikverein Spiez.
Am 5.11.2026 hat Yves den Bauplan «Mitglieder-Import, Mitgliedschaftsformen und Mitgliederportal für Haushalte» freigegeben (92 Entscheide D1–D92; docs/bauplan-mitglieder-import.html und Kurzfassung docs/konzept-mitglieder-import.md im Repo). Der Wizard oben bleibt bis Etappe 3 in Betrieb; was sich ändert, steht auf der Seite Mitgliedschaften (Formen E/V/H, Haken am Link, Eintritt/Austritt).
Warum ein Neuentwurf: Der heutige Wizard legt eine Person je Zeile an (die Cluster-Engine erkennt Eltern-Spalten, ihr Ergebnis wird verworfen), kennt kein Geburtsdatum als Personenfeld (landet als Datums-Zusatzspalte an der Mitgliedschaft, darum hat J+S einen eigenen Nachtrag), keine Pflichtfelder ausser «Nummer und Status höchstens einmal», zählt Nummern still ab 1000 hoch und macht jeden unbekannten Status still «aktiv». Eltern-Kind-Vereine brauchen eine Form, in der eine Zeile ein Kind mit bis zu zwei Erziehungsberechtigten ist, ohne dass Eltern Vereinsjahre sammeln.
Etappe 1 «Fundament» (v0.28.126) im Import:
apps/importer/tests/ hält den Stand v0.28.125 mit 26 Charakterisierungstests fest (Analyse der Beispieldatei, voller Ablauf über die Views, stille Entscheide, Sperre), vorher gab es keinen einzigen Test. Jede Verhaltensänderung in Etappe 3 muss diese Tests bewusst anpassen.birthdate, entry_date, gender, salutation, ahv_number (Präfix 756 mit Prüfziffer) und ein erweiterter exit_date; Vornamen-Ableitung akzent-tolerant. Übergang: die Werte landen heute noch wie bisher (Datum an der Mitgliedschaft, Geschlecht als Auswahl, AHV als Text), aber nicht mehr falsch (vorher wurde m/w eine Ja/Nein-Checkbox und «Mitglied seit» ein Telefon).apps/importer/required_fields.py je Form: nur der Name sperrt; alles andere warnt mit Zähler (Lehre v0.16.9). Geburtsdatum ist nie Pflicht (D30).apps/importer/testdata.py, mit erwarteten Import-Zahlen für die Etappe-3-Tests.Was danach kommt: Etappe 2 ist seit v0.28.127 live (unten); Etappe 3 ersetzt den Wizard durch drei Kacheln (Einzelmitglieder / «Turnende mit Erziehungsberechtigten» / Familien & Haushalte), Spielfiguren für Haushalte, ein Excel-Raster mit einem Zielfeld je Schritt, eine Status-Tabelle mit drei Bedeutungen und einen Schritt je übrige Spalte mit grossen Typ-Buttons.
Was kann es: Nach einem Import (und für Bestandsdaten) sammelt die Seite Mitglieder › Aktionen › Import nachbearbeiten (/clucore/mitglieder/nachbearbeiten/, Admin-only) alle Prüfpunkte als Kacheln mit Zähler, nach vier Prioritäten (R31, D71): 1 verhindert Post oder Geld (Mitgliedschaften ohne erreichbare Person, Zahler ohne PLZ und Ort, mögliche Dubletten), 2 Datenqualität mit Folgen für Addons (abgeleitete Geschlechter prüfen — Abhaken löscht die Herkunft, Teilnehmende ohne Geburtsdatum, Zusatzspalte in Systemfeld umwandeln, Spalten verdichten, «Person 2» aus dem Canvas wird Erziehungsberechtigte), 3 einrichten (Beitragscode ohne Artikel, Bezeichnungen der Personen-Positionen = Blocknamen, Erwachsene mit E-Mail einladen — nur Auswahl prüfen, keine Mail von hier), 4 Komfort und Information (Spalte in Gruppen umwandeln — Werkzeug folgt in Etappe 4, Änderungen aus dem Mitgliederportal der letzten 7 Tage). Erledigt = Zähler 0. Jede Kachel ist zugleich ein ToDo auf der Übersicht im bestehenden Hinweis-Kachel-Stil (D82): die Hinweis-Registry apps/core/hints.py kennt neu priority und zeigt nur die niedrigste Stufe mit offenen Einträgen; «Später» (Wegklick je Benutzer) zählt nicht als erledigt; ein Klick öffnet die ganze Seite.
Zwei Spalten-Werkzeuge (apps/columns/api.py): «Zusatzspalte in Systemfeld umwandeln» (D70) hebt eine Text-, Datums-, Zahl- oder Auswahlspalte nach Person.birthdate/year_of_birth, Link-Eintritt (valid_from der teilnehmenden Links), Person.gender oder Person.ahv_number — mit Probelauf («29 von 31 umwandelbar, 2 unklar»); die Spalte verschwindet nur bei 100 %, sonst bleibt sie mit den unklaren Werten. «Spalten verdichten» (D76) fasst zwei bis n Marker-Spalten («Trompete», «Klarinette», «Posaune» mit «x») zu einer Mehrfachauswahl oder einem Dropdown zusammen; die alten Spaltennamen werden die Optionen, die alten Spalten werden als «aufgelöst» archiviert (dissolved_into), gespeicherte Tabellenansichten zeigen die neue Spalte. Dieselbe Erkennung bietet der Wizard in Etappe 3 beim Status (C7) und bei den übrigen Spalten (C8).
Warum so: Die Ergebnis-Seite des Imports soll nur die Kurzbilanz zeigen; alles Prüfwürdige meldet sich gestaffelt, damit der Admin nicht überschwemmt wird (K24: die Registry kannte weder Priorität noch Obergrenze). Werkzeuge statt Lücken-Kacheln, weil jede Kachel eine Handlung braucht.
Ausserdem in Etappe 2: Der Reset heisst «Alle Mitglieder löschen» mit Bestätigungswort «löschen» (bis v0.28.163 «Alle Mitglieder archivieren» / «ARCHIVIEREN»; seit v0.28.165 sagt die Oberfläche überall «löschen», siehe Entscheidungen); Felddefinitionen überleben ihn, nur Werte verschwinden (Einstellungs-Zeiger, Faktura-Regeln und Tabellenansichten bleiben nach dem Neu-Import gültig). Der Grundsatz «Nur Erst-Import» oben gilt weiter, aber archivierte Mitgliedschaften zählen nicht als Bestand.
Was kann es (aus User-Sicht): Ein neuer Verein landet auf «Mitglieder» zuerst bei der Formwahl (D42): drei grosse Kacheln mit Figuren-Szene — Einzelmitglieder, «Turnende mit Erziehungsberechtigten» (Vereinswort, D57), Familien & Haushalte — mit zwei Sätzen Erklärung und dem Hinweis, dass Notfallkontakte Kontaktangaben und Leiterinnen keine Personen der Mitgliedschaft sind (D50). Danach die Gabel «Liste hochladen», «Einzeln erfassen», «Beispieldatei für diese Form». In Form H folgen die Spielfiguren (C2): höchstens n Personen je Zeile (1–6), Figuren mit der Tätigkeit der Vereinsart (= macht mit), neutrale Figur (= Erziehungsberechtigte), Kind ohne Teilnahme, Hörer (= Kontaktangabe) in Felder ziehen; «Bezeichnungen anpassen» öffnet die Blocknamen. Upload (C4): Form-Chip, Dropzone (20 MB, xlsx/xls/csv), Blattwahl bei mehreren Blättern, Beispieldatei je Form mit Blatt «Erklärung» aus dem Pflichtfeld-Register, Block «Was wir brauchen», Hinweis «n künftige Termine mit Empfänger ‹Ganzer Verein› — der Import trägt neue Personen dort still ein». Prüfbildschirm (C5): «31 Zeilen. 24 haben Erziehungsberechtigte. 7 haben keine und werden Einzelmitglieder», drei echte Zeilen neben ihrer Symbolzeile, Verletzungen der Form («zwei Vornamen in der Spalte Kind» → Form wechseln), Plausibilitätsfrage «Stimmt, weiter / Das stimmt nicht».
Dieser Schritt ist seit v0.28.169 neu gebaut. Der aktuelle Stand steht unten im Abschnitt «Spalten zuordnen: zwei Schritte statt Fragenliste». Der folgende Absatz beschreibt den Stand von v0.28.128.
Spalten zuordnen (C6–C8): ein Zielfeld je Schritt. Oben die Frage («Welche Spalte enthält die E-Mail-Adresse von Erziehungsberechtigte 1?») mit «Wir glauben: Spalte H ‹Mail Erz.ber. 1› · 92 %», unten die hochgeladene Tabelle als Excel-Raster (Spaltenbuchstaben, Zeilennummern, Gruppenüberschrift mit Originalfarbe, Füllgrad, Suche, 5–8 Zeilen). Klick auf den Spaltenkopf ist die Antwort; zugewiesene Spalten tragen ein Etikett und sind gedimmt. «Gibt es nicht» bietet nur die erlaubten Füllstrategien (B5): leer lassen (mit Hinweis), ein Wert für alle (nie für E-Mail, Adresse, Geburtsdatum, Name), hochzählen ab N (nur Mitgliedernummer), ableiten (Geschlecht aus dem Vornamen, Jahrgang ↔ Geburtsdatum, Nachname der Eltern aus dem Kind), gleich wie Teilnehmende (Adresse). Sicher Erkanntes (≥ 85 %) läuft als Einzeiler mit anhaltbarem Weiter in 2 s, Schalter «Erkanntes automatisch weiter», Knopf «Alles Erkannte übernehmen» (P1); die Schrittliste links springt zurück. Status-Werte-Tabelle (C7, D69, D81): jeder Wert der Datei mit Häufigkeit (Tippvarianten zusammengeführt), Bedeutung aktiv / passiv / ausgetreten, Haken «Wert behalten» → Zusatzspalte «Mitgliederart»; drei Marker-Spalten mit «x» werden als ein Status angeboten («Ja, zusammenfassen»). Übrige Spalten (C8, D74): je Spalte ein Schritt mit grossen Typ-Buttons (Text, Auswahl, Mehrfachauswahl, Ja/Nein, Datum, Zahl, Systemfeld, Kontaktangabe, Nicht übernehmen), beste Option vorausgewählt mit Begründung, Behälter-Zeile «Mitgliedschaft | Person, die turnt» nach der Behälter-Regel (Personen-Typen an die Slot-Person, alles Vertragliche an die Mitgliedschaft), «Die restlichen n Spalten wie vorgeschlagen übernehmen» (D87). Unter 1024 px: Banner «Am Laptop geht es leichter …» und Spaltenliste als Karten; alles bedienbar (D67).
Abschluss: Bis v0.28.177 stand hier zuerst der Schritt Vorgabe beim Anlegen (C9: Rechnung an, Mitgliederportal, Termine, Vereinsinfos, dazu Zusatzfelder der Mitgliedschaft und der Person mit vier Modi; D75, D86). Seit v0.28.178 ist er aus dem Import heraus, siehe unten «Abschluss ohne den Schritt «Vorgabe»». Vorschau & Bilanz (C10): Zähler, Sammelentscheide («Anna Muster steht in den Zeilen 3 und 4 bei Lea und Tim — einmal anlegen, alle so machen»; ohne E-Mail nur Vorschlag über Name und Adresse, P7), Plausibilität (Geburtsdatum in der Zukunft «2013 übernehmen / leer lassen», Erziehungsberechtigte jünger als Kind, Füllgrad, zweistellige Jahre mit Pivot), darunter die bisherige Vorschau mit Excel-Export und E-Mail-Dubletten-Dialog. Import durchführen (bis v0.28.176 mit dem Zusatz «still, keine Mails» auf dem Knopf): eine Transaktion mit Savepoint je Zeile, Abbruch ab 50 Fehlern (nichts gespeichert), null Mails; Einladungen zu künftigen «Ganzer Verein»-Terminen entstehen erst nach dem Commit und still (B11). Ergebnis (C11): Kurzbilanz («31 Mitgliedschaften und 55 Personen angelegt · zu 12 Terminen eingetragen, keine Mails verschickt · 7 Zeilen ohne Erziehungsberechtigte · 3 Erziehungsberechtigte wiederverwendet»), Protokoll, Verweis auf die ToDos unter «Import nachbearbeiten».
Was der Motor schreibt (apps/importer/execute_slots.py): je Zeile eine Mitgliedschaft (in H Bezeichnung «Familie <häufigster Nachname>», D55), Personen je Slot, Links mit «macht mit» / «betreut» nach Slot-Art, Etikett aus den Blocknamen, Eintritt (valid_from) nur bei Teilnehmenden, Status/Austritt aus der Tabelle, Vorgabe beim Anlegen (seit v0.28.178 die Einstellung des Vereins: der Motor wendet sie an und schreibt sie nicht mehr), Wiederverwendung von Erziehungsberechtigten per E-Mail (immer) und per Name + Adresse (nur mit Entscheid), Kontaktangabe als Zusatzfeld «Name · Telefon», Marker-Spalten verdichtet, Systemfelder (Geburtsdatum, Jahrgang, Geschlecht mit Herkunft, AHV), Herkunft import_batch an Mitgliedschaft, Link und Person (für «Diesen Import rückgängig», Etappe 4), derived_fields an der Person für Abgeleitetes. Batches ohne Form (vor v0.28.128) laufen über den alten Motor.
Technik: ClubImportBatch speichert jede Antwort (form, figures, block_labels, answers, status_map, rest_columns, defaults (seit v0.28.178 nicht mehr gelesen), decisions, pivot_year, current_step, result); ein Abbruch verliert nichts. Logik apps/importer/wizard.py (Schrittplan, Vorschläge je Slot über Rollenwörter/Gruppen-Header/Farbe, Status-Werte, Rest-Vorschläge, Prüfung, Bilanz), JSON-API apps/importer/wizard_api.py unter /api/import/<batch>/… (plan, antwort, alle-erkannten, zurueck, status, rest, defaults (bis v0.28.177), bilanz, entscheid, blatt, figuren, blocknamen, zeilen), Seiten apps/importer/wizard_views.py + wizard_views_zuordnen.py, Templates templates/importer/, Vue-Komponenten static/js/cluhu-import-wizard.js und cluhu-import-grid.js, Figuren static/img/figuren/*.svg (eine je Vereinsart-Figur, D65). Tests: apps/importer/tests/ 192 (Charakterisierungstests auf den neuen Fluss umgeschrieben; «30 von 30» gilt weiter; Jugendriege und Paarliste aus testdata.py als Ende-zu-Ende-Erwartungen).
Warum so: Der Admin fühlt sich in seiner eigenen Tabelle wohl und definiert Block für Block (D13); eine Tabelle mit 23 Dropdowns war das fremde Vokabular des Systems. Die Formwahl vor dem Upload entscheidet, was der Wizard fragt (D23). Pflicht sperrt nur beim Namen (D6, Lehre v0.16.9). Nie stillschweigend: jeder Automatismus zeigt sich als Einzeiler oder Zähler; unbekannte Statuswerte landen auf Wunsch in «Mitgliederart» statt still «aktiv». Der stille Import ist zwingend (D15): ein Import von 100 Personen in einen Verein mit 12 Terminen hätte sonst bis zu 1200 Mails ausgelöst.
Bewusst nicht gebaut: Klick-Prototyp mit zwei Vereinsadmins (F2 entfällt, D93); «Zeile aufteilen» im Prüfbildschirm (ehrlicher Hinweis statt Werkzeug). Ergänzungsimport, «Diesen Import rückgängig» und der Spaltentyp «Kontaktangabe» kamen mit Etappe 4 (unten).
import_batch / created_via_import_batch); Personen, die vorher schon existierten oder in anderen Mitgliedschaften stehen (wiederverwendete Erziehungsberechtigte), bleiben; Blocker (offene Rechnungen, Spesen …) verhindern das Ganze mit Klartextliste; Felddefinitionen bleiben; der Lauf hat danach den Status undone. Route clucore/mitglieder/importieren/<batch>/rueckgaengig/, Logik apps/importer/undo.py.decisions['mode']='supplement'. Zeilen werden über Mitgliedernummer, sonst E-Mail der Teilnehmenden, sonst Vorname + Nachname + Geburtsdatum bestehenden Mitgliedschaften zugeordnet; bei Treffern werden nur leere Felder gefüllt (Kernfelder, Adresse/E-Mail/Telefon nur wenn fehlend, leere Zusatzfeld-Zellen, fehlende Betreuende angelegt), Haken, Rechnungs-Radio und Status bleiben unangetastet; Zeilen ohne Treffer werden neue Mitgliedschaften. Prüfbildschirm und Bilanz zählen «n Zeilen passen zu bestehenden Mitgliedschaften (a Nummer, b E-Mail, c Name + Geburtsdatum), m neue»; die Kurzbilanz trägt result['supplement']. Rückgängig archiviert nur neu Angelegtes; ergänzte Werte lassen sich nicht zurücknehmen (die Ergebnis-Seite sagt es). Logik apps/importer/supplement.py + Ergänzungsmodus in execute_slots.py.hebe_systemfelder --club <slug> [--apply] [--field birthdate|year_of_birth|entry_date|all] findet Zusatzspalten mit Geburtsdatum-/Jahrgang-/Eintritts-Titeln, macht den Probelauf der Systemfeld-Umwandlung je Spalte (Protokoll ohne Personendaten) und hebt mit --apply die Werte ins Systemfeld; ohne --club Probelauf über alle Vereine.derived_fields: Nachname der Eltern aus dem Kind, Adresse übernommen, Jahrgang aus dem Geburtsdatum) bestätigen oder korrigieren; Abhaken löscht die Herkunft (D35).apps/core/views.py (57 Definitionen), der alte Motorpfad und sechs Templates sind gelöscht; die Vorschau lebt in apps/importer/preview.py, die Feldtypen in apps/importer/field_types.py. Alte Routen-Namen leiten auf den neuen Wizard. Import-Entwürfe aus der Zeit vor v0.28.128 werden beim Einstieg mit Hinweis verworfen. Der Verdrahtungs-Canvas bleibt als «Erweitert» ohne Addon.Die erste Testperson hielt den Import für fertig, obwohl die halbe Fragenliste noch offen war. Yves hat darauf in zwei Runden entschieden, wie der Schritt aussehen soll (Bauplan docs/bauplan-spalten-zuordnen.html, Fassung 2, Antworten Q1 Y1 R1 M2 X1). Gebaut in vier Etappen: Z1 «Felder zuordnen» am Desktop (v0.28.169), Z2 «Übrige Spalten» (v0.28.170), Z3 graue Spalten und Ersatz bei der Checkbox (v0.28.171), Z4 Handy-Fassung und Aufräumen (v0.28.172). Der Abschnitt «Spalten zuordnen» unter Etappe 3 beschreibt den Stand davor.
Was kann es (aus User-Sicht):
Warum so gebaut, und warum nicht anders:
fieldfit.py fragt dieselben Parser wie execute_slots. So kann die Ansicht nichts versprechen, was der Import nicht hält.cellcheck.py) benutzt auch der Import.100vh mit eingeklappter Adressleiste. Solange die Leiste sichtbar ist, läge der untere Rand eines Rahmens mit 100vh samt «Zurück» und «Weiter» unter ihr. v0.28.173 hat das zuerst nur für diese zwei Seiten behoben. Seit v0.28.174 folgt der Rahmen aller CluCore-Seiten der sichtbaren Höhe (100dvh, Regel in base_app.html), und die zwei Seiten bringen dafür keine eigene Regel mehr mit. Das Warum steht unter Fallstricke, Template-Fallen, Punkt 4. Dazu sind am Handy «Vorname und Nachname tauschen» und die Checkbox «Wert behalten» grösser (36 px Tippfläche). Geprüft mit WebKit im iPhone-Profil; die bewegliche Adressleiste zeigt nur ein echtes Gerät.Bewusst nicht gebaut: «Weiter» jederzeit erlauben, ohne dass alles entschieden ist (5B, abgewählt). Felder ohne Spalte gar nicht fragen (1A, abgewählt). Ein Server-Gate vor «Vorschau & Bilanz» (bis v0.28.177 vor der Vorgabe): die Sperre des Knopfs lebt im Browser. Die Vorschau sperrt den Import ohnehin, solange der Name fehlt oder ein erkanntes Feld nicht bestätigt ist.
Technik in einem Satz: static/js/cluhu-import-felder.js und cluhu-import-uebrige.js (je eine Vue-App, Schalter narrow unter 1024 px), gemeinsame Helfer in cluhu-import-helpers.js, Prüfwerke apps/importer/fieldfit.py und cellcheck.py, API oeffnen/, antwort/, rueckgaengig/, status/, uebrige/. Entfallen sind cluhu-import-grid.js, die Endpunkte alle-erkannten/ und alle-ohne-spalte/ und der Schalter «Erkanntes automatisch weiter».
Was sich für den User ändert: Der Knopf auf «Vorschau & Bilanz» heisst nur noch «Import durchführen». Der Satz «Der Import läuft still: keine Mails …» im Kopf der Seite ist weg, die Rückfrage des Browsers lautet «Import jetzt durchführen?». Am Verhalten ändert sich nichts: Der Import verschickt weiterhin keine einzige Mail.
Warum so: «still, keine Mails» beantwortete drei Mal eine Frage, die beim Importieren niemand gestellt hat, und warf dafür neue auf (Rückmeldung Yves, 13.11.2026). «Stiller Import» ist ein Begriff aus dem Bauplan (B11, D15) und gehört nicht auf einen Knopf.
Wo Mails noch genannt werden: nur dort, wo der Import Termine berührt. Der Hinweis auf der Upload-Seite bei künftigen Terminen mit Empfänger «Ganzer Verein» sagt «Der Import trägt neue Personen dort ein, es werden keine Mails verschickt.», und die Kurzbilanz auf der Ergebnis-Seite nennt «keine Mails verschickt» nur zusammen mit «zu n Terminen eingetragen». Ein Verein ohne künftige Termine liest im ganzen Assistenten nichts von Mails.
Warum nicht anders: Den Halbsatz auch bei den Terminen streichen: dort ist die Frage «bekommen jetzt alle eine Einladung?» berechtigt, und ohne Antwort entsteht genau die Unsicherheit, die der Knopf vorher ausgelöst hat.
Erledigt mit v0.28.178: Der Schritt «Vorgabe» unter «3 · Abschluss» ist aus dem Import heraus, siehe nächster Abschnitt.
Was sich für den User ändert: Unter «3 · Abschluss» stehen nur noch «Vorschau & Bilanz» und «Ergebnis». Nach «Übrige Spalten» kommt direkt die Vorschau. Der Import fragt nicht mehr, wer Rechnung, Mitgliederportal, Termine und Vereinsinfos bekommt, und nicht mehr nach Modus und Vorgabewert der Zusatzfelder. Er nimmt dafür die Vorgabe des Vereins, so wie sie beim Durchführen steht. Die Ergebnis-Seite sagt das in einem Absatz und verlinkt die zwei Stellen zum Ändern: Einstellungen › Vereinsdaten › Mitgliedschaften für künftige Mitgliedschaften, Mail › Mitglied-Empfänger für die eben importierten.
Wo die drei Themen jetzt wohnen:
| Thema des früheren Schritts | Neuer Ort |
|---|---|
| Wer bekommt was (Rechnung, Mitgliederportal, Termine, Vereinsinfos) | Einstellungen › Vereinsdaten › Mitgliedschaften, «Vorgabe beim Anlegen» (gab es dort schon seit v0.28.127) |
| Zusatzfelder: Modus und Vorgabewert | Feldverwaltung «eigene Felder», Block «Beim Anlegen» je Feld, und gesammelt unter Vereinsdaten › Mitgliedschaften, «Zusatzfelder beim Anlegen». Beschrieben auf Mitglieder und Mitgliedschaften |
| Zeile «Geburtstag» | entfällt, sie war nur ein Verweis auf das Geburtstags-Addon |
Was der Import mit den Zusatzfeldern macht: Ist eine Spalte der Datei einem Zusatzfeld zugeordnet und das Feld heisst «mit Vorgabe abfragen» oder «stumm befüllen», füllt der Vorgabewert die leeren Zellen dieser Spalte. Das gilt für Felder der Mitgliedschaft und für Felder neu angelegter Personen, nicht im Ergänzungsimport. Ein Feld, das der Import selbst neu anlegt, entsteht mit «abfragen»; einen Vorgabewert bekommt es nachher in der Feldverwaltung.
Warum so: Der Schritt stellte Fragen, die mit der Datei nichts zu tun haben (Rückmeldung Yves, 13.11.2026). Die vier Auswahlen «wer bekommt was» gab es doppelt, und der Import schrieb seine Fassung beim Durchführen in die Vereinseinstellung zurück. Ein Entwurf, der Wochen liegen blieb, überschrieb so eine inzwischen geänderte Vorgabe. Modus und Vorgabewert der Zusatzfelder liessen sich umgekehrt nur hier setzen, also nur während eines Imports, und danach nie mehr ändern. Beides ist eine Einstellung des Vereins und gehört an einen Ort, der immer erreichbar ist.
Warum nicht anders:
ClubImportBatch.defaults bleibt als Feld in der Datenbank, wird aber nicht mehr gelesen. Ein Entwurf von vor v0.28.178 nimmt die Vorgabe des Vereins.…/vorgabe/ löschen: Sie leitet auf den Schritt weiter, bei dem der Import steht. Lesezeichen und alte Links laufen so nicht ins Leere.Technik: apps/importer/wizard.py::step_plan ohne Schritt defaults, defaults_data und set_defaults entfernt, API api/import/<batch>/defaults/ entfernt, wizard_views.vorgabe_view leitet weiter, execute_slots.py liest batch.defaults nicht und schreibt weder MembershipSettings.new_* noch entry_mode/default_value. Templates vorgabe.html und _vorgabe_tabelle.html gelöscht, Absatz data-vorgabe-hinweis in ergebnis.html. Tests: FeldVorgabeImportTests und test_import_laesst_die_vereinsvorgabe_stehen in apps/importer/tests/test_execute_v2.py.
Nebenbei behoben: Das Banner «Import noch nicht abgeschlossen» machte Mitgliederliste und Übersicht am Handy breiter als das Bild, wenn der Dateiname lang war und keine Leerzeichen hatte. Der Name darf jetzt mitten im Wort umbrechen.
Was sich für den User ändert: Jeder Schritt des Imports hat unten dieselbe Leiste. Sie bleibt beim Scrollen im Bild.
| Ort in der Leiste | Inhalt |
|---|---|
| links | «Zurück» zum Schritt davor. Auf dem ersten Schritt fehlt es. |
| Mitte | Stand des Schritts, zum Beispiel «Noch 3 Felder offen» |
| rechts | «Abbrechen», daneben der Hauptknopf |
«Felder zuordnen»:
Warum so: Die Schritte sind in mehreren Etappen entstanden, jede hat ihre Knöpfe selbst gesetzt (Rückmeldung Yves, 13.11.2026). Es gab vier Wörter für «weiter» («Weiter», «Stimmt, weiter», «Zum nächsten Schritt», «Liste hochladen»). «Zurück» und «Abbrechen» waren je nach Schritt ein Textlink, ein Knopf oder fehlten, einmal oben, einmal unten. Wer den Import einmal im Jahr macht, soll die Knöpfe auf jedem Schritt am selben Ort finden. Das Blinken war mit 1,8 Sekunden vorbei, bevor der Satz dazu gelesen war. Die Übersichtstabelle wiederholte die Feldliste und sah aus wie eine zweite Stelle zum Bearbeiten.
Warum nicht anders:
Am Handy in «Felder zuordnen» heisst der Hauptknopf «Weiter», solange er von Feld zu Feld führt, und «Nächster Schritt», sobald alle Felder entschieden sind. «Zurück» führt zuerst von Feld zu Feld, von der Liste aus zur Prüfung.
Schrittanzeige: Seit v0.28.180 neu gelöst, siehe nächster Abschnitt.
Technik: Gemeinsames Teil templates/importer/_fuss_css.html (reines CSS: .iz-page, .iz-body, .iz-footer, .iz-mid, .iz-right, .iz-btn), in jedem Schritt im Block extra_head eingebunden. Marker data-wizard-footer, data-wz-back, data-wz-cancel, data-wz-next. wizard_views.upload_view zeigt den Upload mit ?andere=1 auch bei vorhandener Datei (upload_replace_url()), _store_batch_file übernimmt Vorlage und Ergänzungsmodus in den neuen Lauf. static/js/cluhu-import-felder.js: startPulse() 5,8 s, stopPulse(), Animation zblink .8s ease-in-out 7, overview() entfernt. Tests: FussleisteTests in apps/importer/tests/test_wizard_views.py.
Was sich für den User ändert: Jeder Schritt des Imports sieht oben gleich aus und nutzt die ganze Breite des Bildschirms.
| Von oben nach unten | Inhalt |
|---|---|
| Brotkrumen | «Mitglieder › Importieren › » |
| Schrittanzeige | eine Linie mit einem Punkt je Schritt |
| Titel | eine Überschrift und ein Satz dazu |
| Inhalt | die Karten und Tabellen des Schritts |
| Fussleiste | wie seit v0.28.179 |
Feld «Status» in «Felder zuordnen»: Wer das Feld öffnet und eine Spalte wählt, sieht direkt unter der Frage eine Karte «Welcher Status gilt für die Werte in Spalte T ‹Status›?».
| Spalte der Karte | Bedeutung |
|---|---|
| Wert in deiner Liste | der Wert, so wie er in der Datei steht |
| Kommt vor | in wie vielen Zeilen der Wert steht, zum Beispiel «in 24 Zeilen» |
| Status in CluHu | aktiv, passiv oder ausgetreten |
| Zusätzlich behalten | Haken: der Wert kommt zusätzlich in eine Zusatzspalte |
Warum so: Die drei Kästen gaben eine Gliederung vor, die für den Ablauf nichts bedeutet, und der erste Name war falsch: «Upload» ist selbst ein Schritt von «Vor dem Upload» (Rückmeldung Yves, 13.11.2026, Wahl der Variante A aus vier Entwürfen). Breite und Kopf hatte jeder Schritt selbst gesetzt: 64 rem auf fünf Schritten, 72 rem auf der Bilanz, ganze Breite nur in der Zuordnung. Beim Wechsel von Schritt zu Schritt sprang darum der Inhalt. Beim Status stand unter der Tabelle die gewöhnliche Vorschau «Zeile 2 «aktiv» · Zeile 3 «aktiv»». Sie zeigt den rohen Wert und änderte sich nie. Sie sah aus wie ein Zähler, der nicht zählt.
Warum nicht anders:
Technik: wizard_views.STEP_ORDER und build_stepper() liefern eine flache Liste (steps, pos, total, label), die Erreichbarkeit der Schritte ist unverändert. Kopf: templates/importer/_kopf.html mit Tag wizard_kopf und Markup _kopf_markup.html (bisher _stepper.html, wizard_stepper, _stepper_phasen.html). Gerüst und Fussleiste: templates/importer/_rahmen_css.html (bisher _fuss_css.html), Klassen .iz-crumb, .iz-steps, .iz-st, .iz-now, .iz-title. Die Breitenklassen iz-w5 und iz-w6 sind entfernt. Marker data-wizard-crumb, data-wizard-steps, data-step-key, data-wizard-now. Status: statusSum, statusTitle, rowsText() und die Karte [data-status] mit [data-status-sum] und [data-keep] in static/js/cluhu-import-felder.js, Styles .zf-status und .zs-* in templates/importer/zuordnen.html. Tests: test_schrittanzeige_eine_linie, test_schrittanzeige_form_h_mit_vorlage, test_kopf_und_breite_auf_jedem_schritt_gleich.
…/bilanz/ bleibt.Warum so: Der Import liest Datumszellen als Text JJJJ-MM-TT hh:mm:ss. Die Vorschau zeigte diesen Text roh. display_date() in apps/importer/preview.py formt ihn erst am Schluss für die Anzeige um.
Warum nicht anders: Das Datum schon beim Einlesen umformen: Der Import und der Dubletten-Vergleich rechnen mit dem rohen Wert. Eine Änderung dort hätte das Ergebnis des Imports berührt.
Offen: Die Tabelle der eigenen Liste in «Felder zuordnen» und «Übrige Spalten» zeigt Datumswerte weiter roh.
In der Form «Familien & Haushalte» heisst das Feld im Block «Mitgliedschaft» neu «Name der Mitgliedschaft» statt «Bezeichnung». Unter dem Titel steht: «So heisst die Mitgliedschaft in CluHu, zum Beispiel in Listen und Auswahlfeldern.» Die Checkbox heisst bei diesem Feld «Keine Spalte dafür». Darunter steht, was dann gilt: CluHu nennt die Mitgliedschaft nach dem häufigsten Nachnamen, zum Beispiel «Familie Keller».
Warum so: «Bezeichnung» ist das Wort aus dem Datenmodell. Ein Vereinsadmin weiss damit nicht, wofür die Spalte gebraucht wird. «Dieses Feld gibt es in meiner Liste nicht» passte nicht, weil trotzdem ein Name entsteht.
Warum nicht anders: Eine Auswahl, wie der Name ohne Spalte gebildet wird, gibt es bewusst nicht. Der Name lässt sich später an der Mitgliedschaft ändern.