'wholesale', 'buchhandel' => 'bookseller', 'endkunden', 'endkunde', 'direktkunden', 'direktkunde', 'direkt', 'privatkunden', 'privatkunde' => 'direct', 'amazon', 'amazonmarkt', 'amazon marketplace' => 'amazon', default => null, }; } function findCsvHeader(array $headers, array $accepted): ?int { foreach ($headers as $index => $header) { if (in_array(normalizeCsvValue((string) $header), $accepted, true)) { return $index; } } return null; } function parseCsvUpload(array $upload, PDO $pdo): array { if (($upload['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { throw new InvalidArgumentException('Die CSV-Datei konnte nicht hochgeladen werden.'); } if (!is_string($upload['tmp_name'] ?? null) || !is_uploaded_file($upload['tmp_name'])) { throw new InvalidArgumentException('Ungültiger Datei-Upload.'); } if ((int) ($upload['size'] ?? 0) > CSV_MAX_BYTES) { throw new InvalidArgumentException('Die CSV-Datei darf maximal 5 MB groß sein.'); } $handle = fopen($upload['tmp_name'], 'rb'); if ($handle === false) { throw new InvalidArgumentException('Die CSV-Datei konnte nicht gelesen werden.'); } $firstLine = fgets($handle); if ($firstLine === false) { fclose($handle); throw new InvalidArgumentException('Die CSV-Datei ist leer.'); } $delimiterCounts = [ ';' => substr_count($firstLine, ';'), ',' => substr_count($firstLine, ','), "\t" => substr_count($firstLine, "\t"), ]; $delimiter = array_search(max($delimiterCounts), $delimiterCounts, true); rewind($handle); $headers = fgetcsv($handle, 0, $delimiter); if (!is_array($headers)) { fclose($handle); throw new InvalidArgumentException('Die CSV-Kopfzeile konnte nicht gelesen werden.'); } $groupIndex = findCsvHeader($headers, ['kundengruppe', 'kundengruppen', 'gruppe']); $isbnIndex = findCsvHeader($headers, ['isbn', 'isbnean', 'ean', 'isbnprint']); $quantityIndex = findCsvHeader($headers, ['menge', 'anzahl', 'verkaufsmenge', 'quantity']); if ($groupIndex === null || $isbnIndex === null || $quantityIndex === null) { fclose($handle); throw new InvalidArgumentException('Benötigte Spalten fehlen. Erwartet werden: Kundengruppe, ISBN und Menge.'); } $books = $pdo->query('SELECT id, title, isbn_print FROM books WHERE isbn_print IS NOT NULL AND isbn_print <> ""')->fetchAll(); $booksByIsbn = []; foreach ($books as $book) { $booksByIsbn[normalizeIsbn((string) $book['isbn_print'])] = $book; } $aggregated = []; $lineNumber = 1; while (($row = fgetcsv($handle, 0, $delimiter)) !== false) { $lineNumber++; if ($row === [null] || count(array_filter($row, static fn ($value): bool => trim((string) $value) !== '')) === 0) { continue; } $isbn = importIsbn((string) ($row[$isbnIndex] ?? ''), 'ISBN', $lineNumber); $group = trim((string) ($row[$groupIndex] ?? '')); $channel = csvChannel($group); $quantityText = trim((string) ($row[$quantityIndex] ?? '')); $quantity = filter_var($quantityText, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 1000000000]]); if ($isbn === '' || $channel === null || $quantity === false) { throw new InvalidArgumentException("Fehler in CSV-Zeile {$lineNumber}: ISBN, Kundengruppe oder Menge ist ungültig."); } if (!isset($booksByIsbn[$isbn])) { $rawIsbn = trim((string) ($row[$isbnIndex] ?? '')); throw new InvalidArgumentException("Fehler in CSV-Zeile {$lineNumber}: ISBN '{$rawIsbn}' (normalisiert: {$isbn}) ist in TAT nicht als Printbuch angelegt."); } $book = $booksByIsbn[$isbn]; $bookId = (int) $book['id']; $aggregated[$bookId] ??= [ 'book_id' => $bookId, 'title' => $book['title'], 'isbn_print' => $book['isbn_print'], 'wholesale' => 0, 'bookseller' => 0, 'direct' => 0, 'source_rows' => 0, ]; $aggregated[$bookId][$channel] += $quantity; $aggregated[$bookId]['source_rows']++; } fclose($handle); if (!$aggregated) { throw new InvalidArgumentException('Die CSV-Datei enthält keine verarbeitbaren Verkaufszeilen.'); } return array_values($aggregated); } function parseEbookCsvUpload(array $upload, PDO $pdo): array { if (($upload['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK || !is_string($upload['tmp_name'] ?? null) || !is_uploaded_file($upload['tmp_name'])) { throw new InvalidArgumentException('Die eBook-CSV konnte nicht hochgeladen werden.'); } if ((int) ($upload['size'] ?? 0) > CSV_MAX_BYTES) { throw new InvalidArgumentException('Die eBook-CSV darf maximal 5 MB groß sein.'); } $handle = fopen($upload['tmp_name'], 'rb'); if ($handle === false) { throw new InvalidArgumentException('Die eBook-CSV konnte nicht gelesen werden.'); } $firstLine = fgets($handle); if ($firstLine === false) { fclose($handle); throw new InvalidArgumentException('Die eBook-CSV ist leer.'); } $delimiterCounts = [';' => substr_count($firstLine, ';'), ',' => substr_count($firstLine, ','), "\t" => substr_count($firstLine, "\t")]; $delimiter = array_search(max($delimiterCounts), $delimiterCounts, true); rewind($handle); $headers = fgetcsv($handle, 0, $delimiter); $required = ['verkaufsdatum', 'verkaufsweg', 'isbn', 'titel', 'erlös']; if (!is_array($headers)) { fclose($handle); throw new InvalidArgumentException('Die eBook-CSV-Kopfzeile konnte nicht gelesen werden.'); } $indexes = []; foreach ($required as $header) { $index = findCsvHeader($headers, [$header]); if ($index === null) { fclose($handle); throw new InvalidArgumentException('Benötigte eBook-Spalte fehlt: ' . $header . '.'); } $indexes[$header] = $index; } $books = $pdo->query('SELECT id, title, isbn_ebook FROM books WHERE isbn_ebook IS NOT NULL AND isbn_ebook <> ""')->fetchAll(); $booksByIsbn = []; foreach ($books as $book) { $booksByIsbn[normalizeIsbn((string) $book['isbn_ebook'])] = $book; } $aggregated = []; $warnings = []; $lineNumber = 1; while (($row = fgetcsv($handle, 0, $delimiter)) !== false) { $lineNumber++; if ($row === [null] || count(array_filter($row, static fn ($value): bool => trim((string) $value) !== '')) === 0) { continue; } try { $dateText = trim((string) ($row[$indexes['verkaufsdatum']] ?? '')); $date = DateTimeImmutable::createFromFormat('!Y-m-d', $dateText); $dateErrors = DateTimeImmutable::getLastErrors(); if (!$date || ($dateErrors !== false && ($dateErrors['warning_count'] > 0 || $dateErrors['error_count'] > 0)) || $date->format('Y-m-d') !== $dateText) { throw new InvalidArgumentException('Verkaufsdatum muss JJJJ-MM-TT sein.'); } $channel = trim((string) ($row[$indexes['verkaufsweg']] ?? '')); $isbn = importIsbn((string) ($row[$indexes['isbn']] ?? ''), 'ISBN', $lineNumber); $revenueText = trim((string) ($row[$indexes['erlös']] ?? '')); $revenueText = str_replace(['€', ' '], '', $revenueText); if (str_contains($revenueText, ',') && str_contains($revenueText, '.')) { $revenueText = str_replace('.', '', $revenueText); } $revenue = filter_var(str_replace(',', '.', $revenueText), FILTER_VALIDATE_FLOAT); if ($channel === '' || $revenue === false || !is_finite((float) $revenue)) { throw new InvalidArgumentException('Verkaufsweg, ISBN oder Erlös ist ungültig.'); } if (!isset($booksByIsbn[$isbn])) { $rawIsbn = trim((string) ($row[$indexes['isbn']] ?? '')); throw new InvalidArgumentException("ISBN '{$rawIsbn}' (normalisiert: {$isbn}) ist in TAT nicht als eBook angelegt."); } $book = $booksByIsbn[$isbn]; $key = (int) $book['id'] . '|' . $date->format('Y-m-d') . '|' . $channel; $aggregated[$key] ??= [ 'book_id' => (int) $book['id'], 'title' => $book['title'], 'isbn_ebook' => $book['isbn_ebook'], 'sale_date' => $date->format('Y-m-d'), 'sales_channel' => $channel, 'revenue' => 0.0, ]; $aggregated[$key]['revenue'] += (float) $revenue; } catch (Throwable $exception) { $warnings[] = "Zeile {$lineNumber}: {$exception->getMessage()}"; } } fclose($handle); foreach ($aggregated as &$row) { $row['revenue'] = round((float) $row['revenue'], 2); } unset($row); return ['rows' => array_values($aggregated), 'warnings' => $warnings]; } $message = ''; $error = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { verifyCsrf(); try { $action = (string) ($_POST['action'] ?? ''); if ($action === 'save_sales') { $quantities = $_POST['quantity'] ?? []; if (!is_array($quantities) || count($quantities) > 10000) { throw new InvalidArgumentException('Ungültige Verkaufsdaten.'); } $pdo->beginTransaction(); $bookCheck = $pdo->prepare('SELECT id FROM books WHERE id = :id AND isbn_print IS NOT NULL AND isbn_print <> ""'); $statement = $pdo->prepare( 'INSERT INTO book_sales (book_id, wholesale_quantity, bookseller_quantity, direct_quantity, amazon_quantity) VALUES (:book_id, :wholesale, :bookseller, :direct, :amazon) ON DUPLICATE KEY UPDATE wholesale_quantity = VALUES(wholesale_quantity), bookseller_quantity = VALUES(bookseller_quantity), direct_quantity = VALUES(direct_quantity), amazon_quantity = VALUES(amazon_quantity)' ); foreach ($quantities as $bookId => $values) { $bookId = filter_var($bookId, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]); if (!$bookId || !is_array($values)) { throw new InvalidArgumentException('Ungültige Buchdaten.'); } $bookCheck->execute([':id' => $bookId]); if (!$bookCheck->fetchColumn()) { throw new InvalidArgumentException('Es können nur Printbücher abgerechnet werden.'); } $parsed = []; foreach (['wholesale', 'bookseller', 'direct', 'amazon'] as $channel) { $value = filter_var($values[$channel] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 1000000000]]); if ($value === false) { throw new InvalidArgumentException('Verkaufszahlen müssen ganze Zahlen ab 0 sein.'); } $parsed[$channel] = $value; } $statement->execute([ ':book_id' => $bookId, ':wholesale' => $parsed['wholesale'], ':bookseller' => $parsed['bookseller'], ':direct' => $parsed['direct'], ':amazon' => $parsed['amazon'], ]); } $pdo->commit(); $message = 'Verkaufszahlen gespeichert.'; } elseif ($action === 'upload_csv') { $_SESSION[CSV_PREVIEW_SESSION_KEY] = parseCsvUpload($_FILES['sales_csv'] ?? [], $pdo); $message = 'CSV-Datei geprüft. Bitte die Vorschau kontrollieren und anschließend bestätigen.'; } elseif ($action === 'upload_ebook_csv') { $result = parseEbookCsvUpload($_FILES['ebook_sales_csv'] ?? [], $pdo); $_SESSION[EBOOK_CSV_PREVIEW_SESSION_KEY] = $result['rows']; $_SESSION[EBOOK_CSV_WARNINGS_SESSION_KEY] = $result['warnings']; $message = $result['rows'] ? 'eBook-CSV geprüft. Bitte die Vorschau kontrollieren und anschließend bestätigen.' : 'Die eBook-CSV enthält keine importierbaren Zeilen.'; } elseif ($action === 'cancel_ebook_csv') { unset($_SESSION[EBOOK_CSV_PREVIEW_SESSION_KEY], $_SESSION[EBOOK_CSV_WARNINGS_SESSION_KEY]); $message = 'Die eBook-CSV-Vorschau wurde verworfen. Es wurden keine Abrechnungsdaten geändert.'; } elseif ($action === 'reset_ebook_sales') { $pdo->beginTransaction(); $deleted = $pdo->exec('DELETE FROM ebook_sales'); $pdo->commit(); unset($_SESSION[EBOOK_CSV_PREVIEW_SESSION_KEY], $_SESSION[EBOOK_CSV_WARNINGS_SESSION_KEY]); $message = "Die eBook-Abrechnung wurde zurückgesetzt. {$deleted} gespeicherte Zeile(n) wurden gelöscht."; } elseif ($action === 'confirm_ebook_csv') { $preview = $_SESSION[EBOOK_CSV_PREVIEW_SESSION_KEY] ?? null; if (!is_array($preview) || !$preview) { throw new InvalidArgumentException('Es ist keine eBook-CSV-Vorschau vorhanden.'); } $selected = $_POST['ebook_selected'] ?? []; if (!is_array($selected) || !$selected) { throw new InvalidArgumentException('Bitte mindestens eine eBook-Zeile auswählen.'); } $statement = $pdo->prepare( 'INSERT INTO ebook_sales (book_id, sale_date, sales_channel, revenue) VALUES (:book_id, :sale_date, :sales_channel, :revenue) ON DUPLICATE KEY UPDATE revenue = VALUES(revenue)' ); $pdo->beginTransaction(); $count = 0; foreach ($selected as $index) { $index = filter_var($index, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]); if ($index === false || !isset($preview[$index])) { throw new InvalidArgumentException('Ungültige Auswahl in der eBook-CSV-Vorschau.'); } $row = $preview[$index]; $statement->execute([ ':book_id' => (int) $row['book_id'], ':sale_date' => $row['sale_date'], ':sales_channel' => $row['sales_channel'], ':revenue' => number_format((float) $row['revenue'], 2, '.', ''), ]); $count++; } $pdo->commit(); unset($_SESSION[EBOOK_CSV_PREVIEW_SESSION_KEY], $_SESSION[EBOOK_CSV_WARNINGS_SESSION_KEY]); $message = "eBook-CSV für {$count} ausgewählte Zeilen übernommen."; } elseif ($action === 'confirm_csv') { $preview = $_SESSION[CSV_PREVIEW_SESSION_KEY] ?? null; if (!is_array($preview) || !$preview) { throw new InvalidArgumentException('Es ist keine CSV-Vorschau vorhanden.'); } $selected = $_POST['selected'] ?? []; if (!is_array($selected) || !$selected) { throw new InvalidArgumentException('Bitte mindestens ein Buch für den Import auswählen.'); } $byId = []; foreach ($preview as $row) { $byId[(int) $row['book_id']] = $row; } $statement = $pdo->prepare( 'INSERT INTO book_sales (book_id, wholesale_quantity, bookseller_quantity, direct_quantity, amazon_quantity) VALUES (:book_id, :wholesale, :bookseller, :direct, :amazon) ON DUPLICATE KEY UPDATE wholesale_quantity = VALUES(wholesale_quantity), bookseller_quantity = VALUES(bookseller_quantity), direct_quantity = VALUES(direct_quantity), amazon_quantity = VALUES(amazon_quantity)' ); $pdo->beginTransaction(); $count = 0; foreach ($selected as $bookId) { $bookId = filter_var($bookId, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]); if (!$bookId || !isset($byId[$bookId])) { throw new InvalidArgumentException('Ungültige Auswahl in der CSV-Vorschau.'); } $row = $byId[$bookId]; $statement->execute([ ':book_id' => $bookId, ':wholesale' => (int) $row['wholesale'], ':bookseller' => (int) $row['bookseller'], ':direct' => (int) $row['direct'], ':amazon' => (int) $row['amazon'], ]); $count++; } $pdo->commit(); unset($_SESSION[CSV_PREVIEW_SESSION_KEY]); $message = "CSV-Abrechnung für {$count} ausgewählte Bücher übernommen."; } else { throw new InvalidArgumentException('Ungültige Aktion.'); } } catch (Throwable $exception) { if ($pdo->inTransaction()) { $pdo->rollBack(); } $error = $exception instanceof InvalidArgumentException ? $exception->getMessage() : 'Die CSV-Abrechnung konnte nicht verarbeitet werden.'; } } $csvPreview = $_SESSION[CSV_PREVIEW_SESSION_KEY] ?? []; $books = $pdo->query( 'SELECT b.id, b.title, b.isbn_print, b.isbn_ebook, b.selling_price, b.printing_costs, b.amazon_gross, COALESCE(s.wholesale_quantity, 0) AS wholesale_quantity, COALESCE(s.bookseller_quantity, 0) AS bookseller_quantity, COALESCE(s.direct_quantity, 0) AS direct_quantity, COALESCE(s.amazon_quantity, 0) AS amazon_quantity FROM books b LEFT JOIN book_sales s ON s.book_id = b.id WHERE b.isbn_print IS NOT NULL AND b.isbn_print <> "" ORDER BY b.title' )->fetchAll(); $ebookCsvPreview = $_SESSION[EBOOK_CSV_PREVIEW_SESSION_KEY] ?? []; $ebookCsvWarnings = $_SESSION[EBOOK_CSV_WARNINGS_SESSION_KEY] ?? []; $ebookSales = $pdo->query( 'SELECT b.id AS book_id, b.title, b.isbn_ebook, es.sale_date, es.sales_channel, es.revenue FROM ebook_sales es INNER JOIN books b ON b.id = es.book_id ORDER BY es.sale_date, b.title, es.sales_channel' )->fetchAll(); $authorsByBook = []; $authorStatement = $pdo->query( 'SELECT ba.book_id, a.name, ba.margin_percent FROM book_authors ba INNER JOIN authors a ON a.id = ba.author_id ORDER BY ba.book_id, a.name' ); foreach ($authorStatement->fetchAll() as $author) { $authorsByBook[(int) $author['book_id']][] = $author; } $calculations = []; $authorSummary = []; $totalRevenue = 0.0; $totalAuthorPayout = 0.0; foreach ($books as $book) { $netPrice = (float) $book['selling_price'] / (1 + PRINT_VAT_RATE); $printingCosts = (float) $book['printing_costs']; $wholesaleRevenue = (($netPrice * WHOLESALE_RETAINED_RATE) - $printingCosts) * (int) $book['wholesale_quantity']; $booksellerRevenue = (($netPrice * BOOKSELLER_RETAINED_RATE) - $printingCosts) * (int) $book['bookseller_quantity']; $directRevenue = (($netPrice * DIRECT_RETAINED_RATE) - $printingCosts) * (int) $book['direct_quantity']; $amazonRevenue = (float) $book['amazon_gross'] * (int) $book['amazon_quantity']; $printRevenue = $wholesaleRevenue + $booksellerRevenue + $directRevenue; $revenue = $printRevenue + $amazonRevenue; $totalRevenue += $revenue; $authorCalculations = []; foreach ($authorsByBook[(int) $book['id']] ?? [] as $author) { $authorNet = $printRevenue * ((float) $author['margin_percent'] / 100); $authorPayout = $authorNet * (1 + PRINT_VAT_RATE); $amazonAuthorPayout = $amazonRevenue * ((float) $author['margin_percent'] / 100); $totalAuthorPayout += $authorPayout + $amazonAuthorPayout; $authorName = (string) $author['name']; $authorSummary[$authorName] ??= ['books' => [], 'print_payout' => 0.0, 'amazon_payout' => 0.0, 'ebook_payout' => 0.0]; $authorSummary[$authorName]['books'][(int) $book['id']] ??= ['title' => (string) $book['title'], 'wholesale' => 0.0, 'bookseller' => 0.0, 'direct' => 0.0, 'amazon' => 0.0, 'ebook' => 0.0]; $authorSummary[$authorName]['books'][(int) $book['id']]['wholesale'] += $wholesaleRevenue * ((float) $author['margin_percent'] / 100) * (1 + PRINT_VAT_RATE); $authorSummary[$authorName]['books'][(int) $book['id']]['bookseller'] += $booksellerRevenue * ((float) $author['margin_percent'] / 100) * (1 + PRINT_VAT_RATE); $authorSummary[$authorName]['books'][(int) $book['id']]['direct'] += $directRevenue * ((float) $author['margin_percent'] / 100) * (1 + PRINT_VAT_RATE); $authorSummary[$authorName]['books'][(int) $book['id']]['amazon'] += $amazonRevenue * ((float) $author['margin_percent'] / 100); $authorSummary[$authorName]['print_payout'] += $authorPayout; $authorSummary[$authorName]['amazon_payout'] += $amazonAuthorPayout; $authorCalculations[] = ['name' => $authorName, 'margin_percent' => (float) $author['margin_percent'], 'payout' => $authorPayout + $amazonAuthorPayout]; } $calculations[(int) $book['id']] = [ 'wholesale_revenue' => $wholesaleRevenue, 'bookseller_revenue' => $booksellerRevenue, 'direct_revenue' => $directRevenue, 'amazon_revenue' => $amazonRevenue, 'revenue' => $revenue, 'authors' => $authorCalculations, ]; } $ebookCalculations = []; $ebookTotalRevenue = 0.0; $ebookTotalAuthorPayout = 0.0; foreach ($ebookSales as $sale) { $revenue = (float) $sale['revenue']; $ebookTotalRevenue += $revenue; $authorCalculations = []; foreach ($authorsByBook[(int) $sale['book_id']] ?? [] as $author) { $payout = $revenue * ((float) $author['margin_percent'] / 100); $ebookTotalAuthorPayout += $payout; $authorName = (string) $author['name']; $authorSummary[$authorName] ??= ['books' => [], 'print_payout' => 0.0, 'amazon_payout' => 0.0, 'ebook_payout' => 0.0]; $authorSummary[$authorName]['books'][(int) $sale['book_id']] ??= ['title' => (string) $sale['title'], 'wholesale' => 0.0, 'bookseller' => 0.0, 'direct' => 0.0, 'ebook' => 0.0]; $authorSummary[$authorName]['books'][(int) $sale['book_id']]['ebook'] += $payout; $authorSummary[$authorName]['ebook_payout'] += $payout; $authorCalculations[] = [ 'name' => $authorName, 'margin_percent' => (float) $author['margin_percent'], 'payout' => $payout, ]; } $ebookCalculations[] = [ 'title' => $sale['title'], 'isbn_ebook' => $sale['isbn_ebook'], 'sale_date' => $sale['sale_date'], 'sales_channel' => $sale['sales_channel'], 'revenue' => $revenue, 'authors' => $authorCalculations, ]; } $token = csrf(); ?> Abrechnung erstellen – Autorenmargen

Autorenmargen – Admin

CSV-Abrechnung importieren

Erwartete Spalten: Kundengruppe, ISBN und Menge. Unterstützt werden Semikolon-, Komma- und Tabulator-getrennte CSV-Dateien. Kundengruppen werden so zugeordnet: Großhandel → Großhandel, Buchhandel → Buchhandel, Endkunden/Direktkunden → Direkt, Amazon → Amazon.

eBook-CSV importieren

Erwartete Spalten: Verkaufsdatum, Verkaufsweg, ISBN, Titel und Erlös. Der Erlös wird ohne Abzüge mit der Autorenmarge verrechnet; der Verkaufsweg bleibt erhalten. Mehrere Zeilen mit gleichem Datum, eBook und Verkaufsweg werden zusammengefasst.

Übersprungene eBook-CSV-Zeilen

Zeile(n) wurden übersprungen. Die übrigen gültigen Zeilen können übernommen werden.

eBook-CSV-Vorschau

Nur ausgewählte Zeilen werden übernommen. Bereits gespeicherte Daten mit identischem eBook, Verkaufsdatum und Verkaufsweg werden ersetzt.

$row): ?>
ÜbernehmenVerkaufsdatumVerkaufswegeBookISBN eBookErlös

eBook-Abrechnung zurücksetzen

Damit werden alle bereits gespeicherten eBook-Abrechnungszeilen gelöscht. Bücher, Autoren, Margen und Print-Abrechnungen bleiben erhalten.

CSV-Vorschau

Die Mengen werden je ISBN und Kundengruppe zusammengefasst. Nur ausgewählte Zeilen werden beim Bestätigen übernommen; die dort gespeicherten Mengen werden ersetzt.

ÜbernehmenBuchISBN PrintGroßhandelBuchhandelDirektAmazonCSV-Zeilen

Manuelle Verkaufszahlen

Es werden nur Bücher mit Print-ISBN abgerechnet. eBook-only-Bücher bleiben bis zur separaten eBook-Logik ausgeschlossen.

BuchVerkaufspreisAmazon-MargeDruckkostenISBN PrintGroßhandelBuchhandelDirektAmazon
Es sind keine abrechenbaren Printbücher vorhanden.

Auswertung nach Autoren

Je Autor wird jedes Buch separat nach Großhandel, Buchhandel, Direkt, Amazon und eBook ausgewiesen.

$summary): ?>
AutorBuchGroßhandelBuchhandelDirektAmazoneBookGesamt
Gesamt array_sum(array_column($summary['books'], 'wholesale')), $authorSummary))) ?> array_sum(array_column($summary['books'], 'bookseller')), $authorSummary))) ?> array_sum(array_column($summary['books'], 'direct')), $authorSummary))) ?> array_sum(array_column($summary['books'], 'amazon')), $authorSummary))) ?>

Berechnung nach Buch

Die Zwischenwerte werden ungerundet berechnet und erst für die Anzeige auf Cent gerundet.

BuchErlös GroßhandelErlös BuchhandelErlös DirektErlös AmazonGesamterlösAutorenauszahlung
( %):
Kein Autor zugewiesen
Gesamt

eBook-Berechnung

Autorenanteil = Erlös × Autorenmarge / 100. Es werden keine Druck-, Vertriebs- oder sonstigen Kosten abgezogen.

VerkaufsdatumVerkaufswegeBookISBN eBookErlösAutorenanteil
( %):
Kein Autor zugewiesen
Gesamt