FSCash/html/api/v1/buchungen.php
fds2610-bot 03a6cf774d Feature: lesende API für den Buchungsabgleich (Issue #13)
Neue, rein lesende HTTP-Schnittstelle für den Finanzmanager:
GET /api/v1/buchungen sucht Buchungen anhand Zeitraum, Betrag (mit
Toleranz) und Währung; GET /api/v1/status dient als Verbindungstest.
Beide sind per HTTP Basic Auth mit den bestehenden FSCash-Zugangsdaten
geschützt und rechnen die intern positiv geführten Bargeldausgaben in
die vom Finanzmanager erwartete Vorzeichenkonvention (Ausgaben negativ)
um.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 07:31:31 +02:00

81 lines
2.7 KiB
PHP

<?php
/**
* GET /api/v1/buchungen — Buchungssuche für den Abgleich im Finanzmanager
* (Issue #13). Rein lesend, keine Seiteneffekte.
*
* FSCash führt Bargeldausgaben intern als positiven Betrag; der
* Finanzmanager sendet und erwartet Ausgaben negativ. Die Umrechnung
* übernimmt fscash_signed_betrag() in html/lib/fscash.php.
*/
declare(strict_types=1);
require __DIR__ . '/../../config.php';
require_once __DIR__ . '/../../lib/fscash.php';
require_once __DIR__ . '/_auth.php';
header('Content-Type: application/json; charset=utf-8');
fscash_api_require_auth($pdo);
$validated = fscash_validate_buchungen_query($_GET);
if ($validated['error'] !== null) {
http_response_code(400);
echo json_encode(fscash_api_error($validated['error']), JSON_UNESCAPED_UNICODE);
exit;
}
// FSCash speichert ausschließlich EUR-Beträge; andere Währungen liefern
// definitionsgemäß keine Treffer (kein Fehler, siehe Issue #13 Abschnitt 4).
if ($validated['waehrung'] !== 'EUR') {
echo json_encode(['anzahl' => 0, 'buchungen' => []], JSON_UNESCAPED_UNICODE);
exit;
}
try {
$matchParams = [
'von' => $validated['von'],
'bis' => $validated['bis'],
'betrag' => $validated['betrag'],
'toleranz' => $validated['toleranz'],
];
$countStmt = $pdo->prepare('
SELECT COUNT(*)
FROM entries e
WHERE e.date BETWEEN :von AND :bis
AND ABS((-1 * e.amount) - :betrag) <= :toleranz
');
$countStmt->execute($matchParams);
$anzahl = (int)$countStmt->fetchColumn();
$selectStmt = $pdo->prepare('
SELECT e.id, e.date AS datum, e.amount AS betrag, e.subject AS zweck,
c.name AS kategorie, pm.name AS konto
FROM entries e
LEFT JOIN categories c ON e.category = c.category_id
LEFT JOIN payment_methods pm ON e.payment_method = pm.payment_method_id
WHERE e.date BETWEEN :von AND :bis
AND ABS((-1 * e.amount) - :betrag) <= :toleranz
ORDER BY e.date DESC, e.id DESC
LIMIT :limit
');
$selectStmt->bindValue(':von', $validated['von']);
$selectStmt->bindValue(':bis', $validated['bis']);
$selectStmt->bindValue(':betrag', $validated['betrag']);
$selectStmt->bindValue(':toleranz', $validated['toleranz']);
$selectStmt->bindValue(':limit', $validated['limit'], PDO::PARAM_INT);
$selectStmt->execute();
$rows = $selectStmt->fetchAll();
echo json_encode([
'anzahl' => $anzahl,
'buchungen' => array_map('fscash_format_buchung', $rows),
], JSON_UNESCAPED_UNICODE);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(
fscash_api_error('Interner Fehler bei der Buchungssuche.'),
JSON_UNESCAPED_UNICODE
);
}