diff --git a/Changes.md b/Changes.md
index df18f6e..0ec2372 100644
--- a/Changes.md
+++ b/Changes.md
@@ -1,5 +1,24 @@
# Änderungen
+## Umsätze im Banking sammelweise löschen (17.08.2026)
+
+Die Umsatzlisten des Bankings haben jetzt dieselbe Löschfunktion wie ein Kontoblatt: ein
+Ankreuzfeld vor jedem Umsatz, eines in der Titelleiste für alle angezeigten und darüber den
+Knopf **Auswahl löschen**. Bisher ließ sich ein abgerufener Umsatz nur übernehmen oder
+verwerfen – wegräumen ging gar nicht.
+
+- Der Knopf steht in jedem Reiter – *offen*, *übernommen* und *verworfen* –, nennt die Anzahl
+ der Auswahl und bleibt ohne Auswahl gesperrt. Die Rückfrage vor dem Löschen nennt sie
+ ebenfalls.
+- Gelöscht wird allein die abgerufene Zeile. Eine daraus entstandene Buchung bleibt im
+ Kontoblatt stehen; mit dem Umsatz geht aber sein Fingerabdruck, sodass er beim nächsten
+ Abruf wieder hereinkommen kann.
+- Neue Route `POST /banking/umsaetze/loeschen` auf `Bankumsatz::mehrereLoeschen()`; sie kehrt
+ in den Reiter zurück, aus dem gelöscht wurde.
+- Die Liste ist keine Tabelle, sondern eine Folge von Karten. Die Kopfzeile, die eine Tabelle
+ von selbst hätte, gibt es deshalb neu als `.listenkopf`; die Mechanik dahinter ist
+ dieselbe wie im Kontoblatt (`.alle-waehlen` mit `data-waehlt`).
+
## Ruhige Buchungszeilen im Kontoblatt (17.08.2026)
Die Zeilen des Kontoblatts sind jetzt alle gleich hoch – zwei Textzeilen – und ihr Inhalt
diff --git a/docs/banking.md b/docs/banking.md
index e0ce944..a5b42fc 100644
--- a/docs/banking.md
+++ b/docs/banking.md
@@ -242,6 +242,20 @@ Nichts verschwindet von allein: Ein Umsatz, der weder übernommen noch verworfen
offen und steht beim nächsten Öffnen wieder da. Das Cockpit zählt die offenen unter *zum
Abgleich*.
+### Umsätze löschen
+
+Jeder Reiter der Liste – *offen*, *übernommen*, *verworfen* – hat dieselbe Mehrfachauswahl
+wie ein Kontoblatt: ein Ankreuzfeld vor jedem Umsatz, eines in der Titelleiste für alle
+angezeigten und darüber der Knopf **Auswahl löschen** (`POST /banking/umsaetze/loeschen`,
+`Bankumsatz::mehrereLoeschen()`). Der Knopf nennt die Anzahl und bleibt ohne Auswahl
+gesperrt; ohne JavaScript weist der Controller eine leere Auswahl ab.
+
+Gelöscht wird allein die abgerufene Zeile. Eine daraus entstandene Buchung gehört ins
+Kontoblatt und bleibt dort stehen – wer sie loswerden will, löscht sie dort, und dann kehrt
+ihr Umsatz ohnehin in die Vorschlagsliste zurück. Mit dem Umsatz geht aber sein
+Fingerabdruck: Was innerhalb des nächsten Abrufzeitraums liegt, kommt danach wieder herein –
+bei einem verworfenen Umsatz also erneut als offen.
+
## Regeln
Eine Regel prüft ein Muster gegen Empfänger und/oder Verwendungszweck:
@@ -312,6 +326,12 @@ angehängt, und das Speichern des Zugangsformulars lässt die Wahl stehen. Die A
Bank steckt hinter derselben Grenze wie der Dialog – geprüft wird, was mit ihrem Ergebnis
geschieht.
+`tests/28_UmsaetzeLoeschenTest.php` prüft das Sammellöschen: Es trifft genau die
+ausgewählten Zeilen, eine schon erzeugte Buchung überlebt es, jeder Status lässt sich
+löschen, leere und unbekannte Auswahl bleiben folgenlos, eine doppelt gesendete Nummer zählt
+einmal – und die gerenderte Ansicht trägt in jedem Reiter Ankreuzfelder, Titelleiste und
+Knopf, während die leere Liste keinen Knopf zeigt.
+
`tests/19_KontenzuordnungTest.php` nimmt sich den Weg vor, den ein Umsatz nimmt, wenn die
Zuordnung erst nach dem Abruf getroffen wird: Offene Umsätze ziehen mit, übernommene nicht,
und nur das Bankkonto, um das es geht. Dazu das automatische Buchen – mit Regel, ohne Regel,
diff --git a/public/index.php b/public/index.php
index 7f0eb68..3a3f488 100644
--- a/public/index.php
+++ b/public/index.php
@@ -278,6 +278,7 @@ $router->post('/banking/neu', fn() => (new BankingController())->anlegen());
// „/banking/umsaetze“ und „/banking/regeln“.
$router->get('/banking/umsaetze', fn() => (new BankingController())->umsaetze());
$router->post('/banking/umsaetze/alle', fn() => (new BankingController())->alleUebernehmen());
+$router->post('/banking/umsaetze/loeschen', fn() => (new BankingController())->umsaetzeLoeschen());
$router->post('/banking/umsaetze/{id}/uebernehmen',
fn(array $p) => (new BankingController())->uebernehmen($p));
$router->post('/banking/umsaetze/{id}/verwerfen',
diff --git a/public/static/app.css b/public/static/app.css
index f8ad4fc..82b0dfb 100644
--- a/public/static/app.css
+++ b/public/static/app.css
@@ -1637,6 +1637,34 @@ table.bericht .wertlink:hover {
gap: 1rem;
}
+/* Das Ankreuzfeld der Mehrfachauswahl steht vor dem Text und richtet sich an
+ der ersten Zeile aus, nicht an der Mitte der Karte. */
+.umsatzwahl {
+ flex: none;
+ margin-top: .25rem;
+}
+
+/* Titelleiste einer Kartenliste: Sie vertritt die Kopfzeile, die eine Tabelle
+ von selbst hätte – hier steht das Ankreuzfeld „alle wählen“. */
+.listenkopf {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: .35rem .9rem;
+ margin-bottom: .4rem;
+ border-bottom: 1px solid var(--rand);
+ font-size: .85rem;
+ color: var(--text-gedaempft);
+}
+
+.listenkopf .ankreuzfeld {
+ display: flex;
+ align-items: center;
+ gap: .5rem;
+ font-weight: 500;
+}
+
.umsatztext { min-width: 0; }
.umsatztext .nebentext { display: block; font-size: .8rem; }
diff --git a/src/Controllers/BankingController.php b/src/Controllers/BankingController.php
index dc6c81e..df7c300 100644
--- a/src/Controllers/BankingController.php
+++ b/src/Controllers/BankingController.php
@@ -557,6 +557,36 @@ class BankingController extends BaseController
View::redirect('/banking/umsaetze?status=offen', 'Umsatz steht wieder zum Abgleich bereit.');
}
+ /**
+ * Mehrere in der Abgleichliste angekreuzte Umsätze auf einmal löschen.
+ *
+ * Gelöscht wird nur die abgerufene Zeile. Eine daraus entstandene Buchung
+ * bleibt im Kontoblatt stehen – wer sie loswerden will, löscht sie dort.
+ */
+ public function umsaetzeLoeschen(): void
+ {
+ $this->verlangeAnmeldung();
+
+ // Zurück in denselben Reiter, aus dem gelöscht wurde.
+ $status = $this->query('status', 'offen');
+ if (!array_key_exists($status, Bankumsatz::STATUS)) {
+ $status = 'offen';
+ }
+ $ziel = '/banking/umsaetze?status=' . $status;
+
+ $ids = $this->postIds('umsaetze');
+ if ($ids === []) {
+ View::redirect($ziel, 'Kein Umsatz ausgewählt.', 'warnung');
+ return;
+ }
+
+ $anzahl = Bankumsatz::mehrereLoeschen($ids);
+
+ View::redirect($ziel, $anzahl === 1
+ ? 'Umsatz gelöscht.'
+ : $anzahl . ' Umsätze gelöscht.');
+ }
+
/**
* Alles übernehmen, was einen eindeutigen Vorschlag hat.
*
diff --git a/src/Models/Bankumsatz.php b/src/Models/Bankumsatz.php
index a6a925b..7cdb835 100644
--- a/src/Models/Bankumsatz.php
+++ b/src/Models/Bankumsatz.php
@@ -363,4 +363,30 @@ class Bankumsatz
{
Database::execute('DELETE FROM bank_umsaetze WHERE id = ? AND benutzer_id = ?', [$id, Mandant::id()]);
}
+
+ /**
+ * Mehrere Umsätze auf einmal löschen – die Mehrfachauswahl der Abgleichliste.
+ *
+ * Eine schon erzeugte Buchung bleibt stehen: Sie gehört ins Kontoblatt und
+ * wird auch nur dort gelöscht. Weg ist allein die abgerufene Zeile – und mit
+ * ihr ihr Fingerabdruck, weshalb ein gelöschter Umsatz beim nächsten Abruf
+ * wieder hereinkommen kann.
+ *
+ * @param list