| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/seaside2.pacim.de/web/scripts/ |
Upload File : |
<?php
declare(strict_types=1);
/**
* Repariert bestehende AIDA-external_uids auf das neue stabile Format:
*
* external_uid = sha1("AIDA|{cruise_id}|{legacy_booking_no}|{product_base_code}|{parking_count}")
*
* Hintergrund:
* Die alte UID-Logik mischte unstabile Felder (Kabine, Kennzeichen, Namen) in den
* Hash. Zwischen Preview- und Final-Liste konnten sich diese Felder ändern, der
* Storno-Pass erkannte die alte Buchung dann nicht wieder und stornierte sie.
* Außerdem fehlte der Sub-Index (N° of Parking Bookings) für Sub-Buchungen.
* Dieses Skript zieht alle Bestands-Buchungen auf die neue UID-Formel.
*
* product_base_code wird aus den Produkt-Zuordnungen der Buchung hergeleitet:
* booking_products.product_id = 1 → PW50 (Halle)
* booking_products.product_id = 1 + 8 → PW85 (Halle + Valet)
* booking_products.product_id = 2 → PW40 (Außen)
* booking_products.product_id = 2 + 8 → PW75 (Außen + Valet)
*
* parking_count wird aus der internen booking_no geparst (Format DDMMYY-A{N}-tail).
*
* Aufruf:
* php scripts/repair-aida-external-uids.php # DRY-RUN (zeigt was passieren würde)
* php scripts/repair-aida-external-uids.php --apply # tatsächlich ausführen
*/
if (PHP_SAPI !== 'cli') {
fwrite(STDERR, "Bitte nur über die Kommandozeile aufrufen.\n");
exit(1);
}
$root = dirname(__DIR__);
require_once $root . '/app/core/Database.php';
require_once $root . '/app/services/AidaBookingImporter.php';
$apply = in_array('--apply', $argv ?? [], true);
$db = new Database();
$pdo = $db->getConnection();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "SELECT b.id,
b.external_uid AS old_uid,
b.legacy_booking_no AS legacy,
b.booking_no,
b.status,
e.cruise AS cruise,
GROUP_CONCAT(bp.product_id ORDER BY bp.product_id) AS product_ids
FROM bookings b
JOIN events e ON e.id = b.event_id
LEFT JOIN booking_products bp ON bp.booking_id = b.id
WHERE b.source = 'AIDA'
GROUP BY b.id
ORDER BY b.id ASC";
$rows = $pdo->query($sql)->fetchAll();
$baseByProductIds = [
'1' => 'PW50',
'1,8' => 'PW85',
'2' => 'PW40',
'2,8' => 'PW75',
];
$total = count($rows);
$updates = 0;
$skipped = 0;
$alreadyOk = 0;
$conflicts = 0;
$noLegacy = 0;
$noBase = 0;
printf("AIDA UID-Repair · %s · %d Bestands-Buchungen\n",
$apply ? '[APPLY]' : '[DRY-RUN — nichts wird geschrieben]',
$total
);
printf("Formel: sha1(\"AIDA|<cruise>|<legacy_booking_no>|<product_base_code>|<parking_count>\")\n\n");
foreach ($rows as $r) {
$id = (int)$r['id'];
$cruise = (string)($r['cruise'] ?? '');
$oldUid = (string)($r['old_uid'] ?? '');
$lbn = AidaBookingImporter::normalizeBookingNo((string)($r['legacy_booking_no'] ?? ''));
$bookingNo = (string)($r['booking_no'] ?? '');
if ($lbn === '' || $cruise === '') {
$noLegacy++;
printf("[SKIP ] id=%-6d cruise=%-12s Grund: keine legacy_booking_no oder kein cruise\n",
$id, $cruise);
continue;
}
$pids = (string)($r['product_ids'] ?? '');
$base = $baseByProductIds[$pids] ?? '';
if ($base === '') {
$noBase++;
printf("[SKIP ] id=%-6d legacy=%-12s Grund: product_base_code aus product_ids='%s' nicht ableitbar\n",
$id, $lbn, $pids);
continue;
}
// parking_count aus interner booking_no parsen (Format "DDMMYY-A{N}-tail").
$pcToken = AidaBookingImporter::extractParkingCountFromInternalBookingNo($bookingNo);
$newUid = sha1("AIDA|{$cruise}|{$lbn}|{$base}|{$pcToken}");
if ($newUid === $oldUid) {
$alreadyOk++;
continue;
}
$pcLabel = $pcToken !== '' ? $pcToken : '–';
if (!$apply) {
printf("[DRY ] id=%-6d cruise=%-10s legacy=%-12s base=%s pc=%-2s %s → %s\n",
$id, $cruise, $lbn, $base, $pcLabel,
substr($oldUid, 0, 12) ?: '(null)',
substr($newUid, 0, 12)
);
$updates++;
continue;
}
try {
$upd = $pdo->prepare("UPDATE bookings SET external_uid = :u WHERE id = :id AND source = 'AIDA'");
$upd->execute(['u' => $newUid, 'id' => $id]);
$updates++;
printf("[UPDATED ] id=%-6d cruise=%-10s legacy=%-12s base=%s pc=%-2s %s → %s\n",
$id, $cruise, $lbn, $base, $pcLabel,
substr($oldUid, 0, 12) ?: '(null)',
substr($newUid, 0, 12)
);
} catch (PDOException $e) {
// UNIQUE(source, external_uid) — die Ziel-UID gehört bereits einer anderen Zeile.
// Das kann passieren, wenn alte Daten mehrfach mit unterschiedlichen Fallbacks
// angelegt wurden. In diesem Fall manuell aufräumen.
$conflicts++;
printf("[CONFLICT] id=%-6d legacy=%s pc=%s Ziel-UID schon vergeben — manuelle Prüfung nötig (%s)\n",
$id, $lbn, $pcLabel, $e->getCode()
);
}
}
printf("\nFertig. %s\n", $apply ? '[APPLIED]' : '[DRY-RUN]');
printf(" Buchungen total: %d\n", $total);
printf(" Bereits korrekte UID: %d\n", $alreadyOk);
printf(" Migriert/Migration nötig: %d\n", $updates);
printf(" Ohne legacy_booking_no/cruise: %d\n", $noLegacy);
printf(" Ohne ableitbaren product_base: %d\n", $noBase);
printf(" UID-Konflikte (UNIQUE): %d\n", $conflicts);
if (!$apply && $updates > 0) {
printf("\nNochmal mit --apply ausführen, um die Änderungen tatsächlich zu schreiben.\n");
}