Merge pull request 'Fix n.n.: Kontoblatt zeigt IBAN und Bank der Gegenseite im Verwendungszweck-Tooltip' (#83) from fds2610-bot/Finanzmanager:main into main
Reviewed-on: https://codeberg.org/fds2610/Finanzmanager/pulls/83
This commit is contained in:
commit
99f0e78087
9 changed files with 285 additions and 11 deletions
19
Changes.md
19
Changes.md
|
|
@ -1,5 +1,24 @@
|
||||||
# Änderungen
|
# Änderungen
|
||||||
|
|
||||||
|
## IBAN und Bank der Gegenseite im Kontoblatt (27.09.2026)
|
||||||
|
|
||||||
|
Bei einer Buchung aus dem Bankabruf ließ sich oft nicht mehr erkennen, woher sie
|
||||||
|
stammt – der Empfängername allein reicht nicht immer (z. B. bei Überweisungen
|
||||||
|
zwischen eigenen Konten verschiedener Banken).
|
||||||
|
|
||||||
|
- `Services\FinTsAbruf` löst IBAN/Kontonummer und Bankleitzahl bzw. BIC der Gegenseite
|
||||||
|
schon beim Abruf gegen das Bankleitzahlen-Verzeichnis auf (neu:
|
||||||
|
`Bankinstitut::findeNachBic()` / `findeNachBicOderBlz()`) und schreibt beides nach
|
||||||
|
`bank_umsaetze.gegen_iban` / `gegen_bankname`.
|
||||||
|
- `Bankumsatz::uebernehmen()` trägt beides auf die neue Buchung ein (`buchungen.iban`,
|
||||||
|
`buchungen.bankname`), Migration `020_buchung_herkunft.sql`.
|
||||||
|
- Im Kontoblatt zeigt der Verwendungszweck beim Mouseover IBAN und Bank, sofern
|
||||||
|
bekannt (`kontoblatt.js`, `data-herkunft`).
|
||||||
|
- Gilt nur für künftige Bankabrufe – bereits vorhandene Buchungen aus früheren
|
||||||
|
Abrufen (auch die den Anstoß gebende Buchung 31178) kennen die Gegenseite nicht
|
||||||
|
nachträglich, weil sie beim damaligen Abruf nicht mitgeschrieben wurde.
|
||||||
|
- Test in `tests/45_BuchungHerkunftTest.php`.
|
||||||
|
|
||||||
## Umbuchung wird auch von der Gegenkontoseite erkannt (27.09.2026)
|
## Umbuchung wird auch von der Gegenkontoseite erkannt (27.09.2026)
|
||||||
|
|
||||||
Eine Umbuchung gehört ihrem Ursprungskonto; der automatische Abgleich suchte eine
|
Eine Umbuchung gehört ihrem Ursprungskonto; der automatische Abgleich suchte eine
|
||||||
|
|
|
||||||
20
migrations/020_buchung_herkunft.sql
Normal file
20
migrations/020_buchung_herkunft.sql
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
-- IBAN und Bank der Gegenseite, soweit der Bankabruf sie liefert.
|
||||||
|
--
|
||||||
|
-- MT940 und CAMT nennen zur Gegenseite einer Buchung deren Kontonummer (bei
|
||||||
|
-- CAMT die IBAN) und deren Bankleitzahl bzw. BIC – bisher landete das nirgends,
|
||||||
|
-- Nutzer sahen im Kontoblatt nur den Namen. Bei Buchung 31178 („Felix David
|
||||||
|
-- Schepmann“) reichte der Name nicht, um die Herkunft zu erkennen.
|
||||||
|
--
|
||||||
|
-- Aufgelöst wird der Bankname schon beim Abruf (Services\FinTsAbruf) über das
|
||||||
|
-- Bankleitzahlenverzeichnis (Models\Bankinstitut) – nicht erst bei jeder
|
||||||
|
-- Anzeige im Kontoblatt.
|
||||||
|
|
||||||
|
ALTER TABLE bank_umsaetze ADD COLUMN IF NOT EXISTS
|
||||||
|
gegen_iban VARCHAR(34) NOT NULL DEFAULT '' AFTER verwendungszweck;
|
||||||
|
ALTER TABLE bank_umsaetze ADD COLUMN IF NOT EXISTS
|
||||||
|
gegen_bankname VARCHAR(128) NOT NULL DEFAULT '' AFTER gegen_iban;
|
||||||
|
|
||||||
|
ALTER TABLE buchungen ADD COLUMN IF NOT EXISTS
|
||||||
|
iban VARCHAR(34) NOT NULL DEFAULT '' AFTER empfaenger;
|
||||||
|
ALTER TABLE buchungen ADD COLUMN IF NOT EXISTS
|
||||||
|
bankname VARCHAR(128) NOT NULL DEFAULT '' AFTER iban;
|
||||||
|
|
@ -28,15 +28,29 @@
|
||||||
*
|
*
|
||||||
* Kategorie und Klasse tragen ihren Tooltip vom Server; er nennt bei
|
* Kategorie und Klasse tragen ihren Tooltip vom Server; er nennt bei
|
||||||
* Splitbuchungen mehr, als in der Zelle steht.
|
* Splitbuchungen mehr, als in der Zelle steht.
|
||||||
|
*
|
||||||
|
* Der Buchungstext trägt zusätzlich `data-herkunft`: IBAN und Bank der
|
||||||
|
* Gegenseite, sofern der Bankabruf sie kennt. Die stehen unabhängig davon
|
||||||
|
* im Tooltip, ob der Text selbst abgeschnitten ist – sie stecken sonst
|
||||||
|
* nirgends in der Zelle.
|
||||||
*/
|
*/
|
||||||
function tooltipsSetzen() {
|
function tooltipsSetzen() {
|
||||||
tabelle.querySelectorAll('.zellentext').forEach(function (kasten) {
|
tabelle.querySelectorAll('.zellentext').forEach(function (kasten) {
|
||||||
const abgeschnitten = kasten.scrollHeight > kasten.clientHeight + 1
|
const abgeschnitten = kasten.scrollHeight > kasten.clientHeight + 1
|
||||||
|| kasten.scrollWidth > kasten.clientWidth + 1;
|
|| kasten.scrollWidth > kasten.clientWidth + 1;
|
||||||
const text = kasten.textContent.trim();
|
const text = kasten.textContent.trim();
|
||||||
|
const herkunft = kasten.dataset.herkunft || '';
|
||||||
|
|
||||||
|
const zeilen = [];
|
||||||
if (abgeschnitten && text !== '') {
|
if (abgeschnitten && text !== '') {
|
||||||
kasten.title = text;
|
zeilen.push(text);
|
||||||
|
}
|
||||||
|
if (herkunft !== '') {
|
||||||
|
zeilen.push(herkunft);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zeilen.length > 0) {
|
||||||
|
kasten.title = zeilen.join('\n');
|
||||||
} else {
|
} else {
|
||||||
kasten.removeAttribute('title');
|
kasten.removeAttribute('title');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,48 @@ class Bankinstitut
|
||||||
return $zeile === false ? null : $zeile;
|
return $zeile === false ? null : $zeile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Der Eintrag zu einer BIC – für die Gegenseite eines FinTS-Umsatzes:
|
||||||
|
* CAMT nennt dort meist die BIC statt der Bankleitzahl.
|
||||||
|
*
|
||||||
|
* @return array<string,mixed>|null
|
||||||
|
*/
|
||||||
|
public static function findeNachBic(string $bic): ?array
|
||||||
|
{
|
||||||
|
$bic = strtoupper(trim($bic));
|
||||||
|
if ($bic === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$zeile = Database::fetchOne(
|
||||||
|
"SELECT * FROM bankinstitute
|
||||||
|
WHERE bic = ?
|
||||||
|
ORDER BY (pintan_url <> '') DESC, nr
|
||||||
|
LIMIT 1",
|
||||||
|
[$bic]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $zeile === false ? null : $zeile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Der Eintrag zur Gegenseite eines Umsatzes, deren Kennung mal eine
|
||||||
|
* Bankleitzahl ist (MT940), mal eine BIC (CAMT) – nur Ziffern
|
||||||
|
* unterscheidet zuverlässig zwischen beiden, eine BIC beginnt immer mit
|
||||||
|
* vier Buchstaben.
|
||||||
|
*
|
||||||
|
* @return array<string,mixed>|null
|
||||||
|
*/
|
||||||
|
public static function findeNachBicOderBlz(string $code): ?array
|
||||||
|
{
|
||||||
|
$code = trim($code);
|
||||||
|
if ($code === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctype_digit($code) ? self::findeNachBlz($code) : self::findeNachBic($code);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Suche über Bankleitzahl, BIC, Name und Ort.
|
* Suche über Bankleitzahl, BIC, Name und Ort.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,8 @@ class Bankumsatz
|
||||||
Database::execute(
|
Database::execute(
|
||||||
'INSERT INTO bank_umsaetze
|
'INSERT INTO bank_umsaetze
|
||||||
(benutzer_id, zugang_id, konto_id, bank_konto_id, extern_id, buchungstag, wertstellung,
|
(benutzer_id, zugang_id, konto_id, bank_konto_id, extern_id, buchungstag, wertstellung,
|
||||||
betrag, waehrung, empfaenger, verwendungszweck, buchungsart, roh)
|
betrag, waehrung, empfaenger, verwendungszweck, gegen_iban, gegen_bankname, buchungsart, roh)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||||
[
|
[
|
||||||
Mandant::id(),
|
Mandant::id(),
|
||||||
(int)$daten['zugang_id'],
|
(int)$daten['zugang_id'],
|
||||||
|
|
@ -107,6 +107,8 @@ class Bankumsatz
|
||||||
mb_substr((string)($daten['waehrung'] ?? 'EUR'), 0, 3),
|
mb_substr((string)($daten['waehrung'] ?? 'EUR'), 0, 3),
|
||||||
mb_substr(trim((string)($daten['empfaenger'] ?? '')), 0, 255),
|
mb_substr(trim((string)($daten['empfaenger'] ?? '')), 0, 255),
|
||||||
(string)($daten['verwendungszweck'] ?? ''),
|
(string)($daten['verwendungszweck'] ?? ''),
|
||||||
|
mb_substr(strtoupper(trim((string)($daten['gegen_iban'] ?? ''))), 0, 34),
|
||||||
|
mb_substr(trim((string)($daten['gegen_bankname'] ?? '')), 0, 128),
|
||||||
mb_substr(trim((string)($daten['buchungsart'] ?? '')), 0, 100),
|
mb_substr(trim((string)($daten['buchungsart'] ?? '')), 0, 100),
|
||||||
($daten['roh'] ?? null) ?: null,
|
($daten['roh'] ?? null) ?: null,
|
||||||
]
|
]
|
||||||
|
|
@ -286,6 +288,8 @@ class Bankumsatz
|
||||||
'betrag' => (float)$umsatz['betrag'],
|
'betrag' => (float)$umsatz['betrag'],
|
||||||
'empfaenger' => (string)$umsatz['empfaenger'],
|
'empfaenger' => (string)$umsatz['empfaenger'],
|
||||||
'buchungstext' => (string)$umsatz['verwendungszweck'],
|
'buchungstext' => (string)$umsatz['verwendungszweck'],
|
||||||
|
'iban' => (string)($umsatz['gegen_iban'] ?? ''),
|
||||||
|
'bankname' => (string)($umsatz['gegen_bankname'] ?? ''),
|
||||||
'kategorie_id' => $zuordnung['kategorie_id'] ?? null,
|
'kategorie_id' => $zuordnung['kategorie_id'] ?? null,
|
||||||
'klasse_id' => $zuordnung['klasse_id'] ?? null,
|
'klasse_id' => $zuordnung['klasse_id'] ?? null,
|
||||||
'gegenkonto_id' => $zuordnung['gegenkonto_id'] ?? null,
|
'gegenkonto_id' => $zuordnung['gegenkonto_id'] ?? null,
|
||||||
|
|
|
||||||
|
|
@ -101,12 +101,13 @@ class Buchung
|
||||||
return (int)Database::transaktion(static function () use ($felder, $splits, $adressen): int {
|
return (int)Database::transaktion(static function () use ($felder, $splits, $adressen): int {
|
||||||
Database::execute(
|
Database::execute(
|
||||||
'INSERT INTO buchungen
|
'INSERT INTO buchungen
|
||||||
(benutzer_id, konto_id, datum, valuta, betrag, empfaenger, buchungstext, nummer,
|
(benutzer_id, konto_id, datum, valuta, betrag, empfaenger, iban, bankname, buchungstext,
|
||||||
abgeglichen, kategorie_id, klasse_id, gegenkonto_id, gesplittet, quelle, import_hash)
|
nummer, abgeglichen, kategorie_id, klasse_id, gegenkonto_id, gesplittet, quelle, import_hash)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||||
[
|
[
|
||||||
Mandant::id(), $felder['konto_id'], $felder['datum'], $felder['valuta'],
|
Mandant::id(), $felder['konto_id'], $felder['datum'], $felder['valuta'],
|
||||||
$felder['betrag'], $felder['empfaenger'], $felder['buchungstext'], $felder['nummer'],
|
$felder['betrag'], $felder['empfaenger'], $felder['iban'], $felder['bankname'],
|
||||||
|
$felder['buchungstext'], $felder['nummer'],
|
||||||
$felder['abgeglichen'], $felder['kategorie_id'], $felder['klasse_id'],
|
$felder['abgeglichen'], $felder['kategorie_id'], $felder['klasse_id'],
|
||||||
$felder['gegenkonto_id'], $felder['gesplittet'], $felder['quelle'], $felder['import_hash'],
|
$felder['gegenkonto_id'], $felder['gesplittet'], $felder['quelle'], $felder['import_hash'],
|
||||||
]
|
]
|
||||||
|
|
@ -341,21 +342,24 @@ class Buchung
|
||||||
SELECT b.id, b.datum, b.valuta, b.betrag, b.empfaenger, b.buchungstext, b.nummer,
|
SELECT b.id, b.datum, b.valuta, b.betrag, b.empfaenger, b.buchungstext, b.nummer,
|
||||||
b.abgeglichen, b.kategorie_id, b.klasse_id, b.gesplittet, b.quelle,
|
b.abgeglichen, b.kategorie_id, b.klasse_id, b.gesplittet, b.quelle,
|
||||||
b.bargeld_status, b.bargeld_hinweis,
|
b.bargeld_status, b.bargeld_hinweis,
|
||||||
b.gegenkonto_id, b.konto_id, 0 AS gegensicht, NULL AS split_id
|
b.gegenkonto_id, b.konto_id, 0 AS gegensicht, NULL AS split_id,
|
||||||
|
b.iban, b.bankname
|
||||||
FROM buchungen b
|
FROM buchungen b
|
||||||
WHERE b.konto_id = ?
|
WHERE b.konto_id = ?
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT b.id, b.datum, b.valuta, -b.betrag, b.empfaenger, b.buchungstext, b.nummer,
|
SELECT b.id, b.datum, b.valuta, -b.betrag, b.empfaenger, b.buchungstext, b.nummer,
|
||||||
b.abgeglichen, b.kategorie_id, b.klasse_id, b.gesplittet, b.quelle,
|
b.abgeglichen, b.kategorie_id, b.klasse_id, b.gesplittet, b.quelle,
|
||||||
NULL AS bargeld_status, NULL AS bargeld_hinweis,
|
NULL AS bargeld_status, NULL AS bargeld_hinweis,
|
||||||
b.konto_id AS gegenkonto_id, b.konto_id, 1 AS gegensicht, NULL AS split_id
|
b.konto_id AS gegenkonto_id, b.konto_id, 1 AS gegensicht, NULL AS split_id,
|
||||||
|
\'\' AS iban, \'\' AS bankname
|
||||||
FROM buchungen b
|
FROM buchungen b
|
||||||
WHERE b.gegenkonto_id = ? AND b.gesplittet = 0
|
WHERE b.gegenkonto_id = ? AND b.gesplittet = 0
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT b.id, b.datum, b.valuta, -s.betrag, b.empfaenger, s.notiz, b.nummer,
|
SELECT b.id, b.datum, b.valuta, -s.betrag, b.empfaenger, s.notiz, b.nummer,
|
||||||
b.abgeglichen, s.kategorie_id, s.klasse_id, 0 AS gesplittet, b.quelle,
|
b.abgeglichen, s.kategorie_id, s.klasse_id, 0 AS gesplittet, b.quelle,
|
||||||
NULL AS bargeld_status, NULL AS bargeld_hinweis,
|
NULL AS bargeld_status, NULL AS bargeld_hinweis,
|
||||||
b.konto_id AS gegenkonto_id, b.konto_id, 1 AS gegensicht, s.id AS split_id
|
b.konto_id AS gegenkonto_id, b.konto_id, 1 AS gegensicht, s.id AS split_id,
|
||||||
|
\'\' AS iban, \'\' AS bankname
|
||||||
FROM buchung_splits s
|
FROM buchung_splits s
|
||||||
JOIN buchungen b ON b.id = s.buchung_id
|
JOIN buchungen b ON b.id = s.buchung_id
|
||||||
WHERE s.gegenkonto_id = ?
|
WHERE s.gegenkonto_id = ?
|
||||||
|
|
@ -684,6 +688,8 @@ class Buchung
|
||||||
'valuta' => self::istDatum($valuta) ? $valuta : null,
|
'valuta' => self::istDatum($valuta) ? $valuta : null,
|
||||||
'betrag' => $betrag,
|
'betrag' => $betrag,
|
||||||
'empfaenger' => mb_substr(trim((string)($daten['empfaenger'] ?? '')), 0, 255),
|
'empfaenger' => mb_substr(trim((string)($daten['empfaenger'] ?? '')), 0, 255),
|
||||||
|
'iban' => mb_substr(strtoupper(trim((string)($daten['iban'] ?? ''))), 0, 34),
|
||||||
|
'bankname' => mb_substr(trim((string)($daten['bankname'] ?? '')), 0, 128),
|
||||||
'buchungstext' => trim((string)($daten['buchungstext'] ?? '')),
|
'buchungstext' => trim((string)($daten['buchungstext'] ?? '')),
|
||||||
'nummer' => mb_substr(trim((string)($daten['nummer'] ?? '')), 0, 32),
|
'nummer' => mb_substr(trim((string)($daten['nummer'] ?? '')), 0, 32),
|
||||||
'abgeglichen' => !empty($daten['abgeglichen']) ? 1 : 0,
|
'abgeglichen' => !empty($daten['abgeglichen']) ? 1 : 0,
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use Fhp\Options\FinTsOptions;
|
||||||
use Finanzmanager\Auth;
|
use Finanzmanager\Auth;
|
||||||
use Finanzmanager\Models\AbgleichRegel;
|
use Finanzmanager\Models\AbgleichRegel;
|
||||||
use Finanzmanager\Models\Bankabruf;
|
use Finanzmanager\Models\Bankabruf;
|
||||||
|
use Finanzmanager\Models\Bankinstitut;
|
||||||
use Finanzmanager\Models\Bankprotokoll;
|
use Finanzmanager\Models\Bankprotokoll;
|
||||||
use Finanzmanager\Models\Bankumsatz;
|
use Finanzmanager\Models\Bankumsatz;
|
||||||
use Finanzmanager\Models\Bankzugang;
|
use Finanzmanager\Models\Bankzugang;
|
||||||
|
|
@ -387,6 +388,15 @@ class FinTsAbruf
|
||||||
$zweck = trim($umsatz->getMainDescription());
|
$zweck = trim($umsatz->getMainDescription());
|
||||||
$empfaenger = trim($umsatz->getName());
|
$empfaenger = trim($umsatz->getName());
|
||||||
|
|
||||||
|
// Die Kontonummer der Gegenseite – bei CAMT ihre IBAN, bei
|
||||||
|
// MT940 die alte Kontonummer; die Bankleitzahl bzw. BIC daneben
|
||||||
|
// löst sich sofort gegen das Institutsverzeichnis auf, statt bei
|
||||||
|
// jeder Anzeige im Kontoblatt neu nachzuschlagen.
|
||||||
|
$gegenIban = trim($umsatz->getAccountNumber());
|
||||||
|
$gegenBankname = (string)(
|
||||||
|
Bankinstitut::findeNachBicOderBlz($umsatz->getBankCode())['institut'] ?? ''
|
||||||
|
);
|
||||||
|
|
||||||
$externId = Bankumsatz::externId(
|
$externId = Bankumsatz::externId(
|
||||||
(int)$bankkonto['id'],
|
(int)$bankkonto['id'],
|
||||||
$buchungstag->format('Y-m-d'),
|
$buchungstag->format('Y-m-d'),
|
||||||
|
|
@ -405,6 +415,8 @@ class FinTsAbruf
|
||||||
'betrag' => $betrag,
|
'betrag' => $betrag,
|
||||||
'empfaenger' => $empfaenger,
|
'empfaenger' => $empfaenger,
|
||||||
'verwendungszweck' => $zweck,
|
'verwendungszweck' => $zweck,
|
||||||
|
'gegen_iban' => $gegenIban,
|
||||||
|
'gegen_bankname' => $gegenBankname,
|
||||||
'buchungsart' => trim($umsatz->getBookingText()),
|
'buchungsart' => trim($umsatz->getBookingText()),
|
||||||
'roh' => json_encode([
|
'roh' => json_encode([
|
||||||
'iban' => $bankkonto['iban'],
|
'iban' => $bankkonto['iban'],
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Finanzmanager\Models\Adresse;
|
||||||
use Finanzmanager\Models\Wertpapier;
|
use Finanzmanager\Models\Wertpapier;
|
||||||
use Finanzmanager\View;
|
use Finanzmanager\View;
|
||||||
|
|
||||||
|
|
@ -116,6 +117,26 @@ $splituebersicht = static function (array $splitzeilen) use ($kategorieNamen, $k
|
||||||
$bargeldmarke = static fn(array $zeile): string
|
$bargeldmarke = static fn(array $zeile): string
|
||||||
=> View::bargeldMarke($zeile['bargeld_status'] ?? null, $zeile['bargeld_hinweis'] ?? null);
|
=> View::bargeldMarke($zeile['bargeld_status'] ?? null, $zeile['bargeld_hinweis'] ?? null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IBAN und Bank der Gegenseite, soweit der Bankabruf sie geliefert hat – der
|
||||||
|
* Tooltip über dem Verwendungszweck. kontoblatt.js hängt ihn zusätzlich zum
|
||||||
|
* (nur bei abgeschnittenem Text nötigen) vollständigen Text an; ohne
|
||||||
|
* JavaScript bleibt er unsichtbar, wie schon der Tooltip für abgeschnittenen
|
||||||
|
* Text.
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $zeile
|
||||||
|
*/
|
||||||
|
$herkunft = static function (array $zeile): string {
|
||||||
|
$teile = [];
|
||||||
|
if (($zeile['iban'] ?? '') !== '') {
|
||||||
|
$teile[] = 'IBAN ' . Adresse::ibanFormatiert((string)$zeile['iban']);
|
||||||
|
}
|
||||||
|
if (($zeile['bankname'] ?? '') !== '') {
|
||||||
|
$teile[] = (string)$zeile['bankname'];
|
||||||
|
}
|
||||||
|
return implode("\n", $teile);
|
||||||
|
};
|
||||||
|
|
||||||
include __DIR__ . '/../layout/kopf.php';
|
include __DIR__ . '/../layout/kopf.php';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
@ -273,7 +294,7 @@ include __DIR__ . '/../layout/kopf.php';
|
||||||
<?php /* Ungekürzt: Was nicht auf zwei Zeilen passt, schneidet das
|
<?php /* Ungekürzt: Was nicht auf zwei Zeilen passt, schneidet das
|
||||||
Stylesheet ab – der vollständige Text bleibt für den
|
Stylesheet ab – der vollständige Text bleibt für den
|
||||||
Tooltip erhalten. */ ?>
|
Tooltip erhalten. */ ?>
|
||||||
<td class="nebentext buchungstext"><div class="zellentext"><?= View::h($zeile['buchungstext']) ?></div></td>
|
<td class="nebentext buchungstext"><div class="zellentext" data-herkunft="<?= View::h($herkunft($zeile)) ?>"><?= View::h($zeile['buchungstext']) ?></div></td>
|
||||||
<?php
|
<?php
|
||||||
// Die Gegenseite einer fremden Umbuchung nennt das Konto, von dem
|
// Die Gegenseite einer fremden Umbuchung nennt das Konto, von dem
|
||||||
// sie kommt – und zwar dort, wo sonst die Kategorie steht. Ohne
|
// sie kommt – und zwar dort, wo sonst die Kategorie steht. Ohne
|
||||||
|
|
|
||||||
136
tests/45_BuchungHerkunftTest.php
Normal file
136
tests/45_BuchungHerkunftTest.php
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Finanzmanager\Models\Bankinstitut;
|
||||||
|
use Finanzmanager\Models\Bankumsatz;
|
||||||
|
use Finanzmanager\Models\Bankzugang;
|
||||||
|
use Finanzmanager\Models\Buchung;
|
||||||
|
use Finanzmanager\Models\Konto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IBAN und Bank der Gegenseite: Der Bankabruf löst die Bankleitzahl bzw. BIC
|
||||||
|
* der Gegenseite schon beim Schreiben des Umsatzes gegen das Institutsverzeichnis
|
||||||
|
* auf (Models\Bankinstitut); beim Übernehmen wandert beides auf die Buchung, wo
|
||||||
|
* das Kontoblatt es im Tooltip über dem Verwendungszweck zeigt.
|
||||||
|
*/
|
||||||
|
class BuchungHerkunftTest extends TestCase
|
||||||
|
{
|
||||||
|
protected string $testName = 'Test 45: IBAN und Bank der Gegenseite';
|
||||||
|
|
||||||
|
private int $giro = 0;
|
||||||
|
private int $zugang = 0;
|
||||||
|
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$this->mandantAnlegen();
|
||||||
|
|
||||||
|
$this->giro = Konto::anlegen([
|
||||||
|
'name' => 'Herkunftsgiro', 'kontoart' => 'bank', 'anfangssaldo' => 0, 'aktiv' => 1,
|
||||||
|
]);
|
||||||
|
$this->zugang = Bankzugang::anlegen([
|
||||||
|
'name' => 'Herkunftsbank',
|
||||||
|
'bankname' => 'Herkunftsbank AG',
|
||||||
|
'blz' => '12030000',
|
||||||
|
'fints_url' => 'https://banking.example.org/fints',
|
||||||
|
'benutzerkennung' => 'testkunde',
|
||||||
|
'pin' => 'geheim1234',
|
||||||
|
'abrufzeitraum' => 'letzter_abruf',
|
||||||
|
'aktiv' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->findeNachBicUndBlz();
|
||||||
|
$this->uebernehmenTraegtHerkunftEin();
|
||||||
|
$this->ohneGegenseiteBleibtLeer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findeNachBicUndBlz(): void
|
||||||
|
{
|
||||||
|
Bankinstitut::ersetze([[
|
||||||
|
'nr' => 1, 'blz' => '12030000', 'bic' => 'BYLADEM1001',
|
||||||
|
'institut' => 'Deutsche Kreditbank Testinstitut', 'ort' => 'Berlin',
|
||||||
|
'rechenzentrum' => '', 'organisation' => '', 'hbci_adresse' => '',
|
||||||
|
'hbci_version' => '', 'pintan_url' => '', 'pintan_version' => '', 'stand' => null,
|
||||||
|
]]);
|
||||||
|
|
||||||
|
$nachBic = Bankinstitut::findeNachBic('BYLADEM1001');
|
||||||
|
$this->assertNotEmpty($nachBic, 'Die BIC findet das Institut');
|
||||||
|
$this->assertEquals('Deutsche Kreditbank Testinstitut', $nachBic['institut'], 'Mit dem richtigen Namen');
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
'Deutsche Kreditbank Testinstitut',
|
||||||
|
Bankinstitut::findeNachBicOderBlz('12030000')['institut'] ?? '',
|
||||||
|
'Eine reine Ziffernfolge gilt als Bankleitzahl'
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
'Deutsche Kreditbank Testinstitut',
|
||||||
|
Bankinstitut::findeNachBicOderBlz('BYLADEM1001')['institut'] ?? '',
|
||||||
|
'Buchstaben am Anfang gelten als BIC'
|
||||||
|
);
|
||||||
|
$this->assertNull(Bankinstitut::findeNachBicOderBlz(''), 'Ohne Angabe gibt es keinen Treffer');
|
||||||
|
$this->assertNull(Bankinstitut::findeNachBic('XXXXXXXX'), 'Eine unbekannte BIC bleibt ohne Treffer');
|
||||||
|
|
||||||
|
Bankinstitut::leeren();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function uebernehmenTraegtHerkunftEin(): void
|
||||||
|
{
|
||||||
|
$umsatzId = Bankumsatz::anlegen([
|
||||||
|
'zugang_id' => $this->zugang,
|
||||||
|
'konto_id' => $this->giro,
|
||||||
|
'extern_id' => 'test-herkunft-1',
|
||||||
|
'buchungstag' => '2026-09-07',
|
||||||
|
'wertstellung' => '2026-09-07',
|
||||||
|
'betrag' => -5000.00,
|
||||||
|
'empfaenger' => 'Felix David Schepmann',
|
||||||
|
'verwendungszweck' => 'Privatentnahme',
|
||||||
|
'gegen_iban' => 'de36120300001010903027',
|
||||||
|
'gegen_bankname' => 'Deutsche Kreditbank Testinstitut',
|
||||||
|
]);
|
||||||
|
$this->assertNotEmpty($umsatzId, 'Der Umsatz wurde angelegt');
|
||||||
|
|
||||||
|
$umsatz = Bankumsatz::find((int)$umsatzId);
|
||||||
|
$this->assertEquals(
|
||||||
|
'DE36120300001010903027',
|
||||||
|
$umsatz['gegen_iban'],
|
||||||
|
'Die IBAN steht in Großbuchstaben im Umsatz'
|
||||||
|
);
|
||||||
|
|
||||||
|
$buchungId = Bankumsatz::uebernehmen((int)$umsatzId, []);
|
||||||
|
$buchung = Buchung::find($buchungId);
|
||||||
|
|
||||||
|
$this->assertEquals('DE36120300001010903027', $buchung['iban'], 'Die IBAN steht auf der Buchung');
|
||||||
|
$this->assertEquals(
|
||||||
|
'Deutsche Kreditbank Testinstitut',
|
||||||
|
$buchung['bankname'],
|
||||||
|
'Und der aufgelöste Bankname daneben'
|
||||||
|
);
|
||||||
|
|
||||||
|
$zeilen = Buchung::kontoblatt($this->giro);
|
||||||
|
$gefunden = null;
|
||||||
|
foreach ($zeilen as $zeile) {
|
||||||
|
if ((int)$zeile['id'] === $buchungId) {
|
||||||
|
$gefunden = $zeile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->assertNotEmpty($gefunden, 'Die Buchung steht im Kontoblatt');
|
||||||
|
$this->assertEquals('DE36120300001010903027', $gefunden['iban'], 'Mit IBAN im Kontoblatt');
|
||||||
|
$this->assertEquals(
|
||||||
|
'Deutsche Kreditbank Testinstitut',
|
||||||
|
$gefunden['bankname'],
|
||||||
|
'Und Bankname im Kontoblatt'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ohneGegenseiteBleibtLeer(): void
|
||||||
|
{
|
||||||
|
$buchungId = Buchung::anlegen([
|
||||||
|
'konto_id' => $this->giro, 'datum' => '2026-09-08', 'betrag' => -12.00,
|
||||||
|
'empfaenger' => 'Kiosk', 'quelle' => 'manuell',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$buchung = Buchung::find($buchungId);
|
||||||
|
$this->assertEquals('', $buchung['iban'], 'Ohne Bankabruf bleibt die IBAN leer');
|
||||||
|
$this->assertEquals('', $buchung['bankname'], 'Und der Bankname auch');
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue