- ledger_entries als tenant-sichere Buchungstabelle ergaenzen - Legacy-Einzahlungen und Kaffeeverbrauch idempotent spiegeln - M4-Paritaetscheck und Datenmigrationsdoku ergaenzen
213 lines
6.6 KiB
PHP
213 lines
6.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require __DIR__ . '/dev-db.php';
|
|
|
|
function m4_env(string $name, string $default): string
|
|
{
|
|
$value = getenv($name);
|
|
if ($value === false || trim($value) === '') {
|
|
return $default;
|
|
}
|
|
|
|
return trim($value);
|
|
}
|
|
|
|
function m4_fetch_tenant_id(PDO $pdo, string $slug): int
|
|
{
|
|
$stmt = $pdo->prepare('SELECT id FROM tenants WHERE slug = ?');
|
|
$stmt->execute([$slug]);
|
|
$tenantId = $stmt->fetchColumn();
|
|
|
|
if ($tenantId === false) {
|
|
throw new RuntimeException("Tenant '{$slug}' was not found. Run scripts/backfill-default-tenant.php first.");
|
|
}
|
|
|
|
return (int)$tenantId;
|
|
}
|
|
|
|
function m4_count_missing_payment_participants(PDO $pdo, int $tenantId): int
|
|
{
|
|
$stmt = $pdo->prepare(
|
|
'SELECT COUNT(*)
|
|
FROM kl_Einzahlungen e
|
|
LEFT JOIN participants p
|
|
ON p.tenant_id = ?
|
|
AND p.legacy_mitarbeiter_id = e.MitarbeiterID
|
|
WHERE p.id IS NULL'
|
|
);
|
|
$stmt->execute([$tenantId]);
|
|
|
|
return (int)$stmt->fetchColumn();
|
|
}
|
|
|
|
function m4_count_missing_consumption_participants(PDO $pdo, int $tenantId): int
|
|
{
|
|
$stmt = $pdo->prepare(
|
|
'SELECT COUNT(*)
|
|
FROM kl_Kaffeeverbrauch v
|
|
LEFT JOIN participants p
|
|
ON p.tenant_id = ?
|
|
AND p.legacy_mitarbeiter_id = v.MitarbeiterID
|
|
WHERE p.id IS NULL'
|
|
);
|
|
$stmt->execute([$tenantId]);
|
|
|
|
return (int)$stmt->fetchColumn();
|
|
}
|
|
|
|
$pdo = dev_pdo();
|
|
$applied = dev_apply_migrations($pdo);
|
|
$tenantSlug = m4_env('M4_DEFAULT_TENANT_SLUG', m4_env('M3_DEFAULT_TENANT_SLUG', 'default'));
|
|
|
|
$stats = [
|
|
'applied_migrations' => count($applied),
|
|
'stale_payments_removed' => 0,
|
|
'stale_consumptions_removed' => 0,
|
|
'payments_mirrored' => 0,
|
|
'consumptions_mirrored' => 0,
|
|
];
|
|
|
|
try {
|
|
$tenantId = m4_fetch_tenant_id($pdo, $tenantSlug);
|
|
$missingPayments = m4_count_missing_payment_participants($pdo, $tenantId);
|
|
$missingConsumptions = m4_count_missing_consumption_participants($pdo, $tenantId);
|
|
if ($missingPayments > 0 || $missingConsumptions > 0) {
|
|
throw new RuntimeException(
|
|
"Missing participant mappings for {$missingPayments} payment(s) and {$missingConsumptions} consumption(s)."
|
|
);
|
|
}
|
|
|
|
$pdo->beginTransaction();
|
|
|
|
$removeStalePayments = $pdo->prepare(
|
|
"DELETE le
|
|
FROM ledger_entries le
|
|
LEFT JOIN kl_Einzahlungen e
|
|
ON e.EinzahlungsID = le.legacy_id
|
|
WHERE le.tenant_id = ?
|
|
AND le.legacy_table = 'kl_Einzahlungen'
|
|
AND e.EinzahlungsID IS NULL"
|
|
);
|
|
$removeStalePayments->execute([$tenantId]);
|
|
$stats['stale_payments_removed'] = $removeStalePayments->rowCount();
|
|
|
|
$removeStaleConsumptions = $pdo->prepare(
|
|
"DELETE le
|
|
FROM ledger_entries le
|
|
LEFT JOIN kl_Kaffeeverbrauch v
|
|
ON v.VerbrauchID = le.legacy_id
|
|
WHERE le.tenant_id = ?
|
|
AND le.legacy_table = 'kl_Kaffeeverbrauch'
|
|
AND v.VerbrauchID IS NULL"
|
|
);
|
|
$removeStaleConsumptions->execute([$tenantId]);
|
|
$stats['stale_consumptions_removed'] = $removeStaleConsumptions->rowCount();
|
|
|
|
$insertPayments = $pdo->prepare(
|
|
"INSERT INTO ledger_entries
|
|
(tenant_id, participant_id, type, amount_cents, marks_count,
|
|
unit_price_cents, booked_at, source, note, legacy_table, legacy_id)
|
|
SELECT
|
|
p.tenant_id,
|
|
p.id,
|
|
'payment',
|
|
CAST(ROUND(e.Betrag * 100) AS SIGNED),
|
|
NULL,
|
|
NULL,
|
|
e.Datum,
|
|
'legacy_payment',
|
|
NULL,
|
|
'kl_Einzahlungen',
|
|
e.EinzahlungsID
|
|
FROM kl_Einzahlungen e
|
|
JOIN participants p
|
|
ON p.tenant_id = ?
|
|
AND p.legacy_mitarbeiter_id = e.MitarbeiterID
|
|
ON DUPLICATE KEY UPDATE
|
|
participant_id = VALUES(participant_id),
|
|
type = VALUES(type),
|
|
amount_cents = VALUES(amount_cents),
|
|
marks_count = VALUES(marks_count),
|
|
unit_price_cents = VALUES(unit_price_cents),
|
|
booked_at = VALUES(booked_at),
|
|
source = VALUES(source),
|
|
note = VALUES(note)"
|
|
);
|
|
$insertPayments->execute([$tenantId]);
|
|
|
|
$insertConsumptions = $pdo->prepare(
|
|
"INSERT INTO ledger_entries
|
|
(tenant_id, participant_id, type, amount_cents, marks_count,
|
|
unit_price_cents, booked_at, source, note, legacy_table, legacy_id)
|
|
SELECT
|
|
p.tenant_id,
|
|
p.id,
|
|
'consumption',
|
|
-CAST(ROUND(v.Kosten * 100) AS SIGNED),
|
|
v.AnzahlStriche,
|
|
CAST(ROUND(v.KostenproStrich * 100) AS SIGNED),
|
|
v.Datum,
|
|
CASE
|
|
WHEN v.Eintragsart = 2 THEN 'legacy_web'
|
|
ELSE 'legacy_manual'
|
|
END,
|
|
CASE
|
|
WHEN v.Eintragsart IS NULL THEN NULL
|
|
ELSE CONCAT('Legacy Eintragsart: ', v.Eintragsart)
|
|
END,
|
|
'kl_Kaffeeverbrauch',
|
|
v.VerbrauchID
|
|
FROM kl_Kaffeeverbrauch v
|
|
JOIN participants p
|
|
ON p.tenant_id = ?
|
|
AND p.legacy_mitarbeiter_id = v.MitarbeiterID
|
|
ON DUPLICATE KEY UPDATE
|
|
participant_id = VALUES(participant_id),
|
|
type = VALUES(type),
|
|
amount_cents = VALUES(amount_cents),
|
|
marks_count = VALUES(marks_count),
|
|
unit_price_cents = VALUES(unit_price_cents),
|
|
booked_at = VALUES(booked_at),
|
|
source = VALUES(source),
|
|
note = VALUES(note)"
|
|
);
|
|
$insertConsumptions->execute([$tenantId]);
|
|
|
|
$stmt = $pdo->prepare(
|
|
"SELECT COUNT(*)
|
|
FROM ledger_entries
|
|
WHERE tenant_id = ?
|
|
AND legacy_table = 'kl_Einzahlungen'"
|
|
);
|
|
$stmt->execute([$tenantId]);
|
|
$stats['payments_mirrored'] = (int)$stmt->fetchColumn();
|
|
|
|
$stmt = $pdo->prepare(
|
|
"SELECT COUNT(*)
|
|
FROM ledger_entries
|
|
WHERE tenant_id = ?
|
|
AND legacy_table = 'kl_Kaffeeverbrauch'"
|
|
);
|
|
$stmt->execute([$tenantId]);
|
|
$stats['consumptions_mirrored'] = (int)$stmt->fetchColumn();
|
|
|
|
$pdo->commit();
|
|
} catch (Throwable $e) {
|
|
if ($pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
|
|
fwrite(STDERR, "M4 ledger backfill failed: {$e->getMessage()}\n");
|
|
exit(1);
|
|
}
|
|
|
|
echo "M4 ledger backfill complete.\n";
|
|
echo "Tenant: {$tenantSlug}\n";
|
|
echo "Applied migrations: {$stats['applied_migrations']}\n";
|
|
echo "Stale payments removed: {$stats['stale_payments_removed']}\n";
|
|
echo "Stale consumptions removed: {$stats['stale_consumptions_removed']}\n";
|
|
echo "Payments mirrored: {$stats['payments_mirrored']}\n";
|
|
echo "Consumptions mirrored: {$stats['consumptions_mirrored']}\n";
|