Files
kaffeekasse-saas/scripts/check-payment-matching.php
T

226 lines
6.9 KiB
PHP

<?php
declare(strict_types=1);
if (PHP_SAPI !== 'cli') {
http_response_code(403);
exit("Dieses Skript ist nur fuer die Kommandozeile gedacht.\n");
}
require __DIR__ . '/dev-db.php';
require __DIR__ . '/../app/imports.php';
function payment_matching_assert(string $label, bool $condition, array &$failures, int &$passes): void
{
if ($condition) {
$passes++;
echo "PASS {$label}\n";
return;
}
$failures[] = $label;
echo "FAIL {$label}\n";
}
function payment_matching_insert_tenant(PDO $pdo, string $suffix, string $key): int
{
$stmt = $pdo->prepare('INSERT INTO tenants (slug, name, status) VALUES (?, ?, ?)');
$stmt->execute(["payment-match-{$key}-{$suffix}", "Payment Match {$key}", 'active']);
return (int)$pdo->lastInsertId();
}
function payment_matching_insert_participant(PDO $pdo, int $tenantId, string $displayName, string $email, ?string $paypalName): int
{
$stmt = $pdo->prepare(
'INSERT INTO participants (tenant_id, display_name, email, email_norm, paypal_name, active) VALUES (?, ?, ?, ?, ?, 1)'
);
$stmt->execute([$tenantId, $displayName, $email, strtolower($email), $paypalName]);
return (int)$pdo->lastInsertId();
}
function payment_matching_cleanup(PDO $pdo, array $tenantIds): void
{
foreach ($tenantIds as $tenantId) {
$pdo->prepare(
'DELETE r FROM payment_import_rows r
JOIN payment_import_batches b ON b.id = r.batch_id
WHERE b.tenant_id = ?'
)->execute([$tenantId]);
$pdo->prepare('DELETE FROM payment_import_batches WHERE tenant_id = ?')->execute([$tenantId]);
$pdo->prepare('DELETE FROM participants WHERE tenant_id = ?')->execute([$tenantId]);
$pdo->prepare('DELETE FROM tenants WHERE id = ?')->execute([$tenantId]);
}
}
$pdo = dev_pdo();
$suffix = bin2hex(random_bytes(4));
$failures = [];
$passes = 0;
$tenantIds = [];
try {
$tenantId = payment_matching_insert_tenant($pdo, $suffix, 'a');
$otherTenantId = payment_matching_insert_tenant($pdo, $suffix, 'b');
$tenantIds = [$tenantId, $otherTenantId];
$uniqueId = payment_matching_insert_participant(
$pdo,
$tenantId,
"Eindeutig {$suffix}",
"unique-{$suffix}@test.local",
"PayPal Unique {$suffix}"
);
payment_matching_insert_participant(
$pdo,
$tenantId,
"Doppelt {$suffix}",
"duplicate-one-{$suffix}@test.local",
null
);
payment_matching_insert_participant(
$pdo,
$tenantId,
"Doppelt {$suffix}",
"duplicate-two-{$suffix}@test.local",
null
);
payment_matching_insert_participant(
$pdo,
$tenantId,
"Paypal A {$suffix}",
"paypal-a-{$suffix}@test.local",
"PayPal Doppelt {$suffix}"
);
payment_matching_insert_participant(
$pdo,
$tenantId,
"Paypal B {$suffix}",
"paypal-b-{$suffix}@test.local",
"PayPal Doppelt {$suffix}"
);
$otherTenantUniqueId = payment_matching_insert_participant(
$pdo,
$otherTenantId,
"Doppelt {$suffix}",
"other-{$suffix}@test.local",
null
);
$uniqueByName = imports_find_participant($pdo, $tenantId, "Eindeutig {$suffix}");
payment_matching_assert(
'eindeutiger Anzeigename wird gefunden',
$uniqueByName !== null && $uniqueByName['participant_id'] === $uniqueId,
$failures,
$passes
);
$uniqueByPaypalName = imports_find_participant($pdo, $tenantId, "paypal unique {$suffix}");
payment_matching_assert(
'eindeutiger PayPal-Name wird case-insensitive gefunden',
$uniqueByPaypalName !== null && $uniqueByPaypalName['participant_id'] === $uniqueId,
$failures,
$passes
);
payment_matching_assert(
'doppelter Anzeigename bleibt unmatched',
imports_find_participant($pdo, $tenantId, "Doppelt {$suffix}") === null,
$failures,
$passes
);
payment_matching_assert(
'doppelter PayPal-Name bleibt unmatched',
imports_find_participant($pdo, $tenantId, "PayPal Doppelt {$suffix}") === null,
$failures,
$passes
);
$otherTenantMatch = imports_find_participant($pdo, $otherTenantId, "Doppelt {$suffix}");
payment_matching_assert(
'gleicher Name in anderem Mandanten bleibt eindeutig',
$otherTenantMatch !== null && $otherTenantMatch['participant_id'] === $otherTenantUniqueId,
$failures,
$passes
);
$suggestions = imports_suggest_participants($pdo, $tenantId, "paypal unique {$suffix}", null, 3);
payment_matching_assert(
'Vorschlaege enthalten den passenden Teilnehmer',
$suggestions !== [] && $suggestions[0]['participant_id'] === $uniqueId,
$failures,
$passes
);
$batchId = imports_create_batch($pdo, $tenantId, 'matching-test.csv', hash('sha256', $suffix), null);
$unmatchedRowId = imports_store_row(
$pdo,
$batchId,
1,
null,
"PayPal Unique {$suffix}",
500,
date('Y-m-d H:i:s'),
'unmatched',
['row' => 1]
);
$invalidRowId = imports_store_row(
$pdo,
$batchId,
2,
null,
"Kaputt {$suffix}",
0,
date('Y-m-d H:i:s'),
'invalid',
['row' => 2]
);
$assignResult = imports_assign_row($pdo, $tenantId, $batchId, $unmatchedRowId, $uniqueId);
$stmt = $pdo->prepare('SELECT participant_id, status FROM payment_import_rows WHERE id = ?');
$stmt->execute([$unmatchedRowId]);
$assignedRow = $stmt->fetch();
payment_matching_assert(
'unmatched CSV-Zeile kann manuell zugeordnet werden',
$assignResult['ok'] === true
&& (int)$assignedRow['participant_id'] === $uniqueId
&& (string)$assignedRow['status'] === 'matched',
$failures,
$passes
);
$ignoreResult = imports_ignore_row($pdo, $tenantId, $batchId, $invalidRowId);
$stmt = $pdo->prepare('SELECT participant_id, status FROM payment_import_rows WHERE id = ?');
$stmt->execute([$invalidRowId]);
$ignoredRow = $stmt->fetch();
payment_matching_assert(
'ungueltige CSV-Zeile kann ignoriert werden',
$ignoreResult['ok'] === true
&& $ignoredRow['participant_id'] === null
&& (string)$ignoredRow['status'] === 'ignored',
$failures,
$passes
);
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
fwrite(STDERR, "Payment matching check failed to run: {$e->getMessage()}\n");
payment_matching_cleanup($pdo, $tenantIds);
exit(1);
}
payment_matching_cleanup($pdo, $tenantIds);
if ($failures !== []) {
echo "\nPayment matching check failed with " . count($failures) . " failure(s):\n";
foreach ($failures as $failure) {
echo "- {$failure}\n";
}
exit(1);
}
echo "\nPayment matching check passed with {$passes} assertions.\n";