Fix: FinTS-Kontostand über dedizierte GetBalance-Abfrage statt Statement-Saldo
This commit is contained in:
parent
2b93d38513
commit
ff4e0aac31
3 changed files with 30 additions and 7 deletions
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
- **`migrations/087_bank_sources_last_balance.sql`:** Neue Spalte `last_balance` in `bank_sources`.
|
- **`migrations/087_bank_sources_last_balance.sql`:** Neue Spalte `last_balance` in `bank_sources`.
|
||||||
- **`src/Models/BankSource.php`:** `updateLastFetched()` nimmt optional den beim Abruf ermittelten Kontostand entgegen und persistiert ihn; ohne neuen Wert bleibt der zuletzt bekannte Stand erhalten (kein Verlust bei Fehlern während des Abrufs).
|
- **`src/Models/BankSource.php`:** `updateLastFetched()` nimmt optional den beim Abruf ermittelten Kontostand entgegen und persistiert ihn; ohne neuen Wert bleibt der zuletzt bekannte Stand erhalten (kein Verlust bei Fehlern während des Abrufs).
|
||||||
- **`src/Services/FinTsFetchService.php`:** Summiert nach dem Abruf pro Konto den Schlusssaldo (`getEndBalance()`, Fallback `getStartBalance()`) des letzten Statements und übergibt ihn an `updateLastFetched()`.
|
- **`src/Services/FinTsFetchService.php`:** Fragt pro Konto zusätzlich per dedizierter `GetBalance`-Aktion (HKSAL) den aktuellen gebuchten Saldo ab und summiert diesen. Fällt nur auf den Schlusssaldo des letzten Statements zurück, wenn die Saldenabfrage fehlschlägt – der ursprüngliche Ansatz (Saldo nur aus dem Statement ableiten) lieferte keinen Wert, wenn die Bank mangels neuer Buchungen kein MT940/CAMT-Statement zurückgab (häufigster Fall bei täglichen Abrufen ohne neue Umsätze).
|
||||||
- **`src/Services/PayPalNvpFetchService.php`:** Der bereits vorhandene `GetBalance`-Aufruf in `testCredentials()` liefert den aktuellen Kontostand (`AMT`), der nun mitgespeichert wird.
|
- **`src/Services/PayPalNvpFetchService.php`:** Der bereits vorhandene `GetBalance`-Aufruf in `testCredentials()` liefert den aktuellen Kontostand (`AMT`), der nun mitgespeichert und im Abruf-Log ausgegeben wird.
|
||||||
- **`src/Views/bank/fints.php`:** Kachel zeigt bei vorhandenem Kontostand rechts neben dem Abrufdatum den Betrag an (gleiche Höhe wie das Datum), mit dem Label „Kontostand" rechtsbündig darüber. Kachelgröße unverändert.
|
- **`src/Views/bank/fints.php`:** Kachel zeigt bei vorhandenem Kontostand rechts neben dem Abrufdatum den Betrag an (gleiche Höhe wie das Datum), mit dem Label „Kontostand" rechtsbündig darüber. Kachelgröße unverändert.
|
||||||
- **`tests/57_BankSourceLastBalanceTest.php`:** Neuer Test prüft, dass `last_balance` beim Abruf gesetzt wird und bei einem Abruf ohne ermittelten Kontostand erhalten bleibt.
|
- **`tests/57_BankSourceLastBalanceTest.php`:** Neuer Test prüft, dass `last_balance` beim Abruf gesetzt wird und bei einem Abruf ohne ermittelten Kontostand erhalten bleibt.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ namespace Steuerkram\Services;
|
||||||
use Steuerkram\Database;
|
use Steuerkram\Database;
|
||||||
use Steuerkram\Models\BankSource;
|
use Steuerkram\Models\BankSource;
|
||||||
|
|
||||||
|
use Fhp\Action\GetBalance;
|
||||||
use Fhp\Action\GetSEPAAccounts;
|
use Fhp\Action\GetSEPAAccounts;
|
||||||
use Fhp\Action\GetStatementOfAccount;
|
use Fhp\Action\GetStatementOfAccount;
|
||||||
use Fhp\BaseAction;
|
use Fhp\BaseAction;
|
||||||
|
|
@ -225,15 +226,34 @@ class FinTsFetchService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$accountBalance = null;
|
||||||
|
try {
|
||||||
|
$balAction = GetBalance::create($account);
|
||||||
|
$fints->execute($balAction);
|
||||||
|
if ($balAction->needsTan()) {
|
||||||
|
if (!$this->handleStrongAuthentication($balAction, $iban . ' (Saldo)')) {
|
||||||
|
throw new \RuntimeException('TAN für Saldenabfrage nicht bestätigt.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($balAction->getBalances() as $hisal) {
|
||||||
|
$accountBalance = ($accountBalance ?? 0.0) + $hisal->getGebuchterSaldo()->getAmount();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$log(' → Saldenabfrage fehlgeschlagen: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Saldo aus dem letzten Statement ableiten, falls die dedizierte
|
||||||
|
// Saldenabfrage (HKSAL) fehlschlägt oder von der Bank nicht unterstützt wird.
|
||||||
$statements = $soa->getStatements();
|
$statements = $soa->getStatements();
|
||||||
if (!empty($statements)) {
|
if ($accountBalance === null && !empty($statements)) {
|
||||||
$lastStatement = end($statements);
|
$lastStatement = end($statements);
|
||||||
$accountBalance = $lastStatement->getEndBalance() ?? $lastStatement->getStartBalance();
|
$accountBalance = $lastStatement->getEndBalance() ?? $lastStatement->getStartBalance();
|
||||||
|
}
|
||||||
|
|
||||||
if ($accountBalance !== null) {
|
if ($accountBalance !== null) {
|
||||||
$balance = ($balance ?? 0.0) + $accountBalance;
|
$balance = ($balance ?? 0.0) + $accountBalance;
|
||||||
$log(' → Kontostand: ' . number_format($accountBalance, 2, ',', '.') . ' €');
|
$log(' → Kontostand: ' . number_format($accountBalance, 2, ',', '.') . ' €');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$accountNew = 0;
|
$accountNew = 0;
|
||||||
foreach ($soa->getStatements() as $statement) {
|
foreach ($soa->getStatements() as $statement) {
|
||||||
|
|
|
||||||
|
|
@ -314,6 +314,9 @@ class PayPalNvpFetchService
|
||||||
}
|
}
|
||||||
if (isset($result['AMT']) && is_numeric($result['AMT'])) {
|
if (isset($result['AMT']) && is_numeric($result['AMT'])) {
|
||||||
$this->lastBalance = (float)$result['AMT'];
|
$this->lastBalance = (float)$result['AMT'];
|
||||||
|
($this->log)('Kontostand: ' . number_format($this->lastBalance, 2, ',', '.') . ' ' . ($result['CURRENCYCODE'] ?? 'EUR'));
|
||||||
|
} else {
|
||||||
|
($this->log)('Kontostand: nicht in GetBalance-Antwort enthalten (Felder: ' . implode(', ', array_keys($result)) . ')');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue