diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8996490..9c3d4db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,19 +1,7 @@
-# Änderungsprotokoll
+# Changelog
-## 2026-09-18
+## 2026-09-23
-Seit Commit `0eb32fb` wurden folgende Änderungen vorgenommen:
-
-- Neue geschützte Admin-Seite `author_price.php` für die Berechnung von Autorenpreisen.
-- Printbücher können per Dropdown ausgewählt werden.
-- Berechnung des Autorenpreises auf Grundlage von Brutto-Preis, 7 % Mehrwertsteuer, Druckkosten und einem frei wählbaren Rabatt.
-- Der Rabatt ist standardmäßig mit 85 % vorbelegt und kann zwischen 0 und 100 % eingegeben werden.
-- Die Ersparnis pro Exemplar wird separat ausgewiesen.
-- Der Autorenrabatt wird als eigener Rechenposten angezeigt.
-- Checkbox ergänzt, um alternativ die kumulierte Autorenmarge aller am Buch beteiligten Autoren als Rabatt zu verwenden.
-- Das manuelle Rabattfeld wird bei aktivierter Margen-Checkbox deaktiviert.
-- Die kumulierte Autorenmarge wird aus allen Buch-Autor-Zuordnungen berechnet.
-- JavaScript für die unmittelbare Aktivierung und Deaktivierung des Rabattfelds ergänzt.
-- Navigation im Admin-Bereich um den Link zum Autorenpreis-Rechner erweitert.
-- CSS für die Anordnung von Rabattfeld und Checkbox ergänzt.
-- PHP-, SQL- und Zugriffsschutz-Prüfungen für die neue Funktion durchgeführt.
+- Amazon-Marge als festen Bruttoerlös pro verkauftem Buch ergänzt.
+- Amazon-Verkaufszahlen, CSV-Zuordnung und Autorenverteilung in die Abrechnung aufgenommen.
+- eBook-CSV-Import, ISBN-Validierung und bestätigungspflichtiges Zurücksetzen der eBook-Abrechnung dokumentiert und abgesichert.
diff --git a/README_SETUP.txt b/README_SETUP.txt
index bc9d40c..81c4839 100644
--- a/README_SETUP.txt
+++ b/README_SETUP.txt
@@ -14,6 +14,20 @@ Einrichtung des Admin-Bereichs
Die Anwendung verwendet PDO mit echten Prepared Statements, CSRF-Schutz,
Session-Härtung und getrennte CSS-/JavaScript-Dateien.
+ISBN-Eingabe und CSV-Import
+
+Im Webformular werden Print- und eBook-ISBN als gültige ISBN-13 mit
+Bindestrichen erwartet, zum Beispiel 978-3-16-148410-0. Beim CSV-Import
+werden Bindestriche und andere Trennzeichen vor dem Abgleich entfernt.
+Eine CSV-ISBN ohne Bindestriche wird daher gegen die formatierte ISBN in
+TAT erkannt. Bei einem nicht gefundenen Treffer wird die betreffende Zeile
+übersprungen. Alle übersprungenen Zeilen und Gründe werden gesammelt und
+unterhalb des Uploads zusammengefasst angezeigt; gültige Zeilen können
+weiterhin übernommen werden. Bereits gespeicherte eBook-Abrechnungen
+können über die ausdrücklich bestätigungspflichtige Funktion „Gesamte
+eBook-Abrechnung zurücksetzen“ gelöscht und anschließend neu importiert
+werden. Print-Abrechnungen und Stammdaten bleiben dabei unverändert.
+
CSV-Import für die Abrechnung
Auf der Seite /tat/billing.php kann eine JTL-Verkaufs-CSV hochgeladen werden.
diff --git a/admin.php b/admin.php
index 50e1e24..63dff2d 100644
--- a/admin.php
+++ b/admin.php
@@ -92,8 +92,11 @@ if ($loggedIn) {
$ebookPrice = filter_var($_POST['ebook_price'] ?? null, FILTER_VALIDATE_FLOAT);
if ($ebookPrice === false || $ebookPrice < 0 || $ebookPrice > 99999999.99) throw new InvalidArgumentException('Der eBook-Preis muss zwischen 0 und 99.999.999,99 liegen.');
$ebookPrice = round((float) $ebookPrice, 2);
- $isbnPrint = postText('isbn_print', 20) ?: null;
- $isbnEbook = postText('isbn_ebook', 20) ?: null;
+ $amazonGross = filter_var($_POST['amazon_gross'] ?? null, FILTER_VALIDATE_FLOAT);
+ if ($amazonGross === false || $amazonGross < 0 || $amazonGross > 99999999.99) throw new InvalidArgumentException('Die Amazon-Marge muss zwischen 0 und 99.999.999,99 liegen.');
+ $amazonGross = round((float) $amazonGross, 2);
+ $isbnPrint = validateIsbnField(postText('isbn_print', 20), 'Die Print-ISBN');
+ $isbnEbook = validateIsbnField(postText('isbn_ebook', 20), 'Die eBook-ISBN');
$authorIds = $_POST['author_id'] ?? [];
$margins = $_POST['margin_percent'] ?? [];
if ($title === '') throw new InvalidArgumentException('Der Buchtitel ist erforderlich.');
@@ -107,13 +110,13 @@ if ($loggedIn) {
}
$pdo->beginTransaction();
if ($id) {
- $statement = $pdo->prepare('UPDATE books SET title = :title, printing_costs = :printing_costs, selling_price = :selling_price, ebook_price = :ebook_price, isbn_print = :isbn_print, isbn_ebook = :isbn_ebook WHERE id = :id');
- $statement->execute([':title' => $title, ':printing_costs' => $printingCosts, ':selling_price' => $sellingPrice, ':ebook_price' => $ebookPrice, ':isbn_print' => $isbnPrint, ':isbn_ebook' => $isbnEbook, ':id' => $id]);
+ $statement = $pdo->prepare('UPDATE books SET title = :title, printing_costs = :printing_costs, selling_price = :selling_price, ebook_price = :ebook_price, amazon_gross = :amazon_gross, isbn_print = :isbn_print, isbn_ebook = :isbn_ebook WHERE id = :id');
+ $statement->execute([':title' => $title, ':printing_costs' => $printingCosts, ':selling_price' => $sellingPrice, ':ebook_price' => $ebookPrice, ':amazon_gross' => $amazonGross, ':isbn_print' => $isbnPrint, ':isbn_ebook' => $isbnEbook, ':id' => $id]);
$bookId = $id;
$pdo->prepare('DELETE FROM book_authors WHERE book_id = :book_id')->execute([':book_id' => $bookId]);
} else {
- $statement = $pdo->prepare('INSERT INTO books (title, printing_costs, selling_price, ebook_price, isbn_print, isbn_ebook) VALUES (:title, :printing_costs, :selling_price, :ebook_price, :isbn_print, :isbn_ebook)');
- $statement->execute([':title' => $title, ':printing_costs' => $printingCosts, ':selling_price' => $sellingPrice, ':ebook_price' => $ebookPrice, ':isbn_print' => $isbnPrint, ':isbn_ebook' => $isbnEbook]);
+ $statement = $pdo->prepare('INSERT INTO books (title, printing_costs, selling_price, ebook_price, amazon_gross, isbn_print, isbn_ebook) VALUES (:title, :printing_costs, :selling_price, :ebook_price, :amazon_gross, :isbn_print, :isbn_ebook)');
+ $statement->execute([':title' => $title, ':printing_costs' => $printingCosts, ':selling_price' => $sellingPrice, ':ebook_price' => $ebookPrice, ':amazon_gross' => $amazonGross, ':isbn_print' => $isbnPrint, ':isbn_ebook' => $isbnEbook]);
$bookId = (int) $pdo->lastInsertId();
}
$statement = $pdo->prepare('INSERT INTO book_authors (book_id, author_id, margin_percent) VALUES (:book_id, :author_id, :margin_percent)');
@@ -144,14 +147,14 @@ $editAuthorId = filter_var($_GET['edit_author'] ?? null, FILTER_VALIDATE_INT, ['
$editBookId = filter_var($_GET['edit_book'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]) ?: null;
$author = ['id' => '', 'name' => '', 'address' => '', 'postal_code' => '', 'city' => '', 'bank_account' => '', 'bic' => '', 'bank_name' => '', 'paypal_address' => ''];
if ($editAuthorId) { $statement = $pdo->prepare('SELECT id, name, address, postal_code, city, bank_account, bic, bank_name, paypal_address FROM authors WHERE id = :id'); $statement->execute([':id' => $editAuthorId]); $author = $statement->fetch() ?: $author; }
-$book = ['id' => '', 'title' => '', 'printing_costs' => '0.00', 'selling_price' => '0.00', 'ebook_price' => '0.00', 'isbn_print' => '', 'isbn_ebook' => ''];
+$book = ['id' => '', 'title' => '', 'printing_costs' => '0.00', 'selling_price' => '0.00', 'ebook_price' => '0.00', 'amazon_gross' => '0.00', 'isbn_print' => '', 'isbn_ebook' => ''];
$bookAuthors = [];
if ($editBookId) {
- $statement = $pdo->prepare('SELECT id, title, printing_costs, selling_price, ebook_price, isbn_print, isbn_ebook FROM books WHERE id = :id'); $statement->execute([':id' => $editBookId]); $book = $statement->fetch() ?: $book;
+ $statement = $pdo->prepare('SELECT id, title, printing_costs, selling_price, ebook_price, amazon_gross, isbn_print, isbn_ebook FROM books WHERE id = :id'); $statement->execute([':id' => $editBookId]); $book = $statement->fetch() ?: $book;
$statement = $pdo->prepare('SELECT ba.author_id, a.name, ba.margin_percent FROM book_authors ba INNER JOIN authors a ON a.id = ba.author_id WHERE ba.book_id = :id ORDER BY a.name'); $statement->execute([':id' => $editBookId]); $bookAuthors = $statement->fetchAll();
}
$authors = $pdo->query('SELECT id, name, address, postal_code, city, bank_account, bic, bank_name, paypal_address FROM authors ORDER BY name')->fetchAll();
-$books = $pdo->query('SELECT b.id, b.title, b.printing_costs, b.selling_price, b.ebook_price, b.isbn_print, b.isbn_ebook, GROUP_CONCAT(CONCAT(a.name, " (", ba.margin_percent, " %)" ) ORDER BY a.name SEPARATOR ", ") AS author_summary FROM books b LEFT JOIN book_authors ba ON ba.book_id = b.id LEFT JOIN authors a ON a.id = ba.author_id GROUP BY b.id ORDER BY b.title')->fetchAll();
+$books = $pdo->query('SELECT b.id, b.title, b.printing_costs, b.selling_price, b.ebook_price, b.amazon_gross, b.isbn_print, b.isbn_ebook, GROUP_CONCAT(CONCAT(a.name, " (", ba.margin_percent, " %)" ) ORDER BY a.name SEPARATOR ", ") AS author_summary FROM books b LEFT JOIN book_authors ba ON ba.book_id = b.id LEFT JOIN authors a ON a.id = ba.author_id GROUP BY b.id ORDER BY b.title')->fetchAll();
$token = csrf();
?>
Autorenmargen – Admin
@@ -160,8 +163,8 @@ $token = csrf();
= e($message) ?>
= e($error) ?>
= $author['id'] ? 'Autor bearbeiten' : 'Autor anlegen' ?>
-
= $book['id'] ? 'Buch bearbeiten' : 'Buch anlegen' ?>
+
= $book['id'] ? 'Buch bearbeiten' : 'Buch anlegen' ?>
Autoren
| Name | Adresse | IBAN | BIC | Bankname | PayPal-Adresse | Aktionen |
|---|
| = e($row['name']) ?> | = e(trim($row['address'] . ', ' . $row['postal_code'] . ' ' . $row['city'], ', ')) ?> | = e($row['bank_account']) ?> | = e($row['bic']) ?> | = e($row['bank_name']) ?> | = e($row['paypal_address']) ?> | Bearbeiten |
-Bücher
| Titel | Druckkosten | Print-Preis | eBook-Preis | ISBN Print | ISBN eBook | Autoren / Margen | Aktionen |
|---|
| = e($row['title']) ?> | = number_format((float) $row['printing_costs'], 2, ',', '.') ?> € | = number_format((float) $row['selling_price'], 2, ',', '.') ?> € | = number_format((float) $row['ebook_price'], 2, ',', '.') ?> € | = e($row['isbn_print']) ?> | = e($row['isbn_ebook']) ?> | = e($row['author_summary'] ?? '') ?> | Bearbeiten |
+Bücher
| Titel | Druckkosten | Print-Preis | eBook-Preis | Amazon-Marge | ISBN Print | ISBN eBook | Autoren / Margen | Aktionen |
|---|
| = e($row['title']) ?> | = number_format((float) $row['printing_costs'], 2, ',', '.') ?> € | = number_format((float) $row['selling_price'], 2, ',', '.') ?> € | = number_format((float) $row['ebook_price'], 2, ',', '.') ?> € | = number_format((float) $row['amazon_gross'], 2, ',', '.') ?> € | = e($row['isbn_print']) ?> | = e($row['isbn_ebook']) ?> | = e($row['author_summary'] ?? '') ?> | Bearbeiten |